package org.argeo.cms.acr; import java.util.AbstractMap; import java.util.AbstractSet; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; import javax.xml.namespace.QName; import org.argeo.api.acr.Content; import org.argeo.api.acr.CrAttributeType; import org.argeo.api.acr.CrName; import org.argeo.api.acr.NamespaceUtils; import org.argeo.api.acr.spi.ProvidedContent; import org.argeo.api.acr.spi.ProvidedSession; import org.argeo.cms.util.LangUtils; /** Partial reference implementation of a {@link ProvidedContent}. */ public abstract class AbstractContent extends AbstractMap implements CmsContent { private final ProvidedSession session; // cache // private String _path = null; public AbstractContent(ProvidedSession session) { this.session = session; } /* * ATTRIBUTES MAP IMPLEMENTATION */ @Override public Set> entrySet() { Set> result = new AttrSet(); return result; } @Override public Object get(Object key) { return get((QName) key, Object.class).orElse(null); } /* * ATTRIBUTES OPERATIONS */ @Override public Class getType(QName key) { return String.class; } @Override public boolean isMultiple(QName key) { return false; } @SuppressWarnings("unchecked") @Override public List getMultiple(QName key, Class clss) { Object value = get(key); if (value == null) return new ArrayList<>(); if (value instanceof List) { if (clss.isAssignableFrom(Object.class)) return (List) value; List res = new ArrayList<>(); List lst = (List) value; for (Object o : lst) { A item = CrAttributeType.cast(clss, o).get(); res.add(item); } return res; } else {// singleton A res = CrAttributeType.cast(clss, value).get(); return Collections.singletonList(res); } } /* * CONTENT OPERATIONS */ @Override public String getPath() { // if (_path != null) // return _path; List ancestors = new ArrayList<>(); collectAncestors(ancestors, this); StringBuilder path = new StringBuilder(); ancestors: for (Content c : ancestors) { QName name = c.getName(); if (CrName.root.qName().equals(name)) continue ancestors; path.append(PATH_SEPARATOR); path.append(NamespaceUtils.toPrefixedName(name)); int siblingIndex = c.getSiblingIndex(); if (siblingIndex != 1) path.append('[').append(siblingIndex).append(']'); } // _path = path.toString(); // return _path; return path.toString(); } private void collectAncestors(List ancestors, Content content) { if (content == null) return; ancestors.add(0, content); collectAncestors(ancestors, content.getParent()); } @Override public int getDepth() { List ancestors = new ArrayList<>(); collectAncestors(ancestors, this); return ancestors.size(); } @Override public boolean isRoot() { return CrName.root.qName().equals(getName()); } @Override public String getSessionLocalId() { return getPath(); } /* * SESSION */ @Override public ProvidedSession getSession() { return session; } /* * TYPING */ @Override public List getContentClasses() { return new ArrayList<>(); } /* * UTILITIES */ // protected boolean isDefaultAttrTypeRequested(Class clss) { // // check whether clss is Object.class // return clss.isAssignableFrom(Object.class); // } // @Override // public String toString() { // return "content " + getPath(); // } /* * DEFAULTS */ // - no children // - no attributes // - cannot be modified @Override public Iterator iterator() { return Collections.emptyIterator(); } @Override public Content add(QName name, QName... classes) { throw new UnsupportedOperationException("Content cannot be added."); } @Override public void remove() { throw new UnsupportedOperationException("Content cannot be removed."); } protected Iterable keys() { return Collections.emptySet(); } @Override public Optional get(QName key, Class clss) { return Optional.empty(); } protected void removeAttr(QName key) { throw new UnsupportedOperationException("Attributes cannot be removed."); } /* * SUB CLASSES */ class AttrSet extends AbstractSet> { @Override public Iterator> iterator() { final Iterator keys = keys().iterator(); Iterator> it = new Iterator>() { QName key = null; @Override public boolean hasNext() { return keys.hasNext(); } @Override public Entry next() { key = keys.next(); // TODO check type Optional value = get(key, Object.class); assert !value.isEmpty(); AbstractMap.SimpleEntry entry = new SimpleEntry<>(key, value.get()); return entry; } @Override public void remove() { if (key != null) { AbstractContent.this.removeAttr(key); } else { throw new IllegalStateException("Iteration has not started"); } } }; return it; } @Override public int size() { return LangUtils.size(keys()); } } /* * OBJECT METHODS */ @Override public String toString() { return "content " + getPath(); } }