package net.ion.craken.node.crud; import java.io.IOException; import java.io.Serializable; import java.io.Writer; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import net.ion.craken.loaders.EntryKey; import net.ion.craken.node.IteratorList; import net.ion.craken.node.ReadNode; import net.ion.craken.node.ReadSession; import net.ion.craken.node.convert.Functions; import net.ion.craken.node.convert.rows.AdNodeRows; import net.ion.craken.node.convert.rows.FieldDefinition; import net.ion.craken.node.crud.tree.ExtendPropertyId; import net.ion.craken.node.crud.tree.Fqn; import net.ion.craken.node.crud.tree.NodeNotExistsException; import net.ion.craken.node.crud.tree.TreeNode; import net.ion.craken.node.crud.tree.impl.PropertyId; import net.ion.craken.node.crud.tree.impl.PropertyId.PType; import net.ion.craken.node.crud.tree.impl.PropertyValue; import net.ion.framework.db.Rows; import net.ion.framework.mte.Engine; import net.ion.framework.parse.gson.JsonObject; import net.ion.framework.util.Debug; import net.ion.framework.util.ListUtil; import net.ion.framework.util.MapUtil; import net.ion.framework.util.ObjectUtil; import net.ion.framework.util.SetUtil; import net.ion.framework.util.StringUtil; import net.ion.nsearcher.config.Central; import net.ion.nsearcher.search.filter.TermFilter; import org.apache.commons.collections.IteratorUtils; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.index.Term; import org.apache.lucene.queryparser.classic.ParseException; import org.apache.lucene.search.Query; import org.apache.lucene.search.QueryWrapperFilter; import org.apache.lucene.search.TermQuery; import org.infinispan.context.Flag; import org.infinispan.io.GridFilesystem; import com.google.common.base.Function; import com.google.common.base.Predicate; import com.google.common.collect.Sets; public class ReadNodeImpl implements ReadNode, Serializable { private static final long serialVersionUID = 1785904048897031227L; private transient ReadSession session; private Fqn fqn; protected ReadNodeImpl(ReadSession session, Fqn fqn) { this.session = session; this.fqn = fqn; } public static ReadNode load(ReadSession session, Fqn fqn) { ReadNodeImpl result = new ReadNodeImpl(session, fqn); // if (result.treeNode() == null) throw new NotFoundPath(fqn) ; return result; } @Override public boolean equals(Object obj) { if (!(obj instanceof ReadNodeImpl)) return false; ReadNodeImpl that = (ReadNodeImpl) obj; return treeNode().equals(that.treeNode()); } @Override public int hashCode() { return fqn.hashCode(); } public String toString() { return this.getClass().getSimpleName() + "[fqn=" + fqn().toString() + "]"; } // only use for test public TreeNode<PropertyId, PropertyValue> treeNode() { TreeNode result = session.workspace().readNode(fqn); if (result == null) return new GhostTreeNode(session, fqn) ; return result; } // .. common public ReadSession session() { return session; } public Fqn fqn() { return fqn; } public int dataSize() { return treeNode().dataSize(); } public ReadNode parent() { return load(session, fqn.getParent()); } public boolean hasChild(String relativeFqn) { String[] names = StringUtil.split(relativeFqn, "/") ; return treeNode().hasChild(Fqn.fromElements(names)); } public ReadNode child(String name) { String[] names = StringUtil.split(name, "/") ; Fqn childFqn = Fqn.fromRelativeElements(fqn, names) ; return session.pathBy(childFqn) ; } public ReadNode root() { return session.root(); } public Set<String> childrenNames() { Set<String> set = SetUtil.orderedSet(SetUtil.newSet()); for (Object object : treeNode().getChildrenNames()) { set.add(ObjectUtil.toString(object)); } return set; } public ReadChildren children() { return new ReadChildren(session, fqn, treeNode().getChildrenFqn().iterator()); } public String asString(String pid) { return property(pid).asString() ; } public String asDateFmt(String pid, String fmt) { return property(pid).asDateFmt(fmt) ; } public <R> R defaultValue(String pid, R val) { return property(pid).defaultValue(val) ; } public ReadNode asRef(String pid, Fqn prefix) { return session.ghostBy(Fqn.fromRelativeElements(prefix, asString(pid))) ; } public PropertyValue property(String key) { return propertyId(PropertyId.normal(key)); } public PropertyValue extendProperty(String propPath) { return ExtendPropertyId.create(propPath).propValue(this); } public PropertyValue propertyId(PropertyId pid) { if (treeNode().get(pid) == null) return PropertyValue.NotFound; return ObjectUtil.coalesce(treeNode().get(pid).workspace(session.workspace()), PropertyValue.NotFound); } // public Optional<PropertyValue> optional(String key) { // return Optional.fromNullable(treeNode.get(PropertyId.normal(key))); // } private GridFilesystem gfs() { return session.workspace().gfs(); } public Set<PropertyId> keys() { TreeNode<PropertyId, PropertyValue> treeNode = treeNode(); return treeNode.getKeys(); } public Set<PropertyId> normalKeys() { return Sets.filter(keys(), new Predicate<PropertyId>() { @Override public boolean apply(PropertyId pid) { return pid.type() == PType.NORMAL; } }); } public Map<PropertyId, PropertyValue> toMap() { if (treeNode() == null) return MapUtil.EMPTY ; return treeNode().getData() ; } public <T> T transformer(Function<ReadNode, T> function) { return function.apply(this); } public Map<String, Object> toPropertyMap(final int descendantDepth) { final int childDepth = descendantDepth - 1; Map<String, Object> result = MapUtil.newMap(); for (Entry<PropertyId, PropertyValue> entry : toMap().entrySet()) { if (entry.getKey().type() == PropertyId.PType.NORMAL) { result.put(entry.getKey().getString(), entry.getValue().asSet().size() <= 1 ? entry.getValue().value() : entry.getValue().asSet()); } else if (entry.getKey().type() == PropertyId.PType.REFER && descendantDepth > 0) { IteratorList<ReadNode> refs = refs(entry.getKey().getString()); Set<Map<String, Object>> set = SetUtil.orderedSet(SetUtil.newSet()); while (refs.hasNext()) { set.add(refs.next().toPropertyMap(childDepth)); } result.put('@' + entry.getKey().getString(), set); } } IteratorList<ReadNode> children = children().iterator(); if (descendantDepth > 0 && children.hasNext()) { while (children.hasNext()) { final ReadNode next = children.next(); result.put('/' + next.fqn().getLastElementAsString(), next.toPropertyMap(childDepth)); } } return Collections.unmodifiableMap(result); } public Object id() { return fqn; } public boolean hasProperty(String pid) { return hasPropertyId(PropertyId.fromIdString(pid)); } public boolean hasPropertyId(PropertyId pid) { return keys().contains(pid); } public boolean hasRef(String refName) { return keys().contains(PropertyId.refer(refName)); } public boolean hasRef(String refName, Fqn fqn) { return propertyId(PropertyId.refer(refName)).asSet().contains(fqn.toString()); } public ReadNode ref(String refName) { // PropertyValue findProp = propertyId(PropertyId.refer(refName)) ; // if (findProp == PropertyValue.NotFound) throw new NodeNotExistsException("not found ref :" + refName) ; // return session().pathBy(Fqn.fromString(findProp.stringValue())); PropertyId referId = PropertyId.refer(refName); if (hasPropertyId(referId)) { String refPath = propertyId(referId).stringValue(); if (StringUtil.isBlank(refPath)) throw new NodeNotExistsException("not found ref :" + refName); return session.ghostBy(refPath); } else { throw new NodeNotExistsException("not found ref :" + refName); } } public NodeIteratorList<ReadNode> refs(String refName) { return NodeIteratorList.ReadRefs(this, refName) ; } public ReadChildren refChildren(String refName) { Iterator<Fqn> titer = treeNode().getReferencesFqn(refName).iterator(); return new ReadChildren(session, fqn, titer); } public WalkRefChildren walkRefChildren(String refName) { return new WalkRefChildren(session, fqn, refName, treeNode().getReferencesFqn(refName).iterator()); } public ReadChildren walkRefMeChildren(String refName, Fqn parentFqn) { ReadNode parent = session.ghostBy(parentFqn) ; ReadNode currentNode = this ; List<Fqn> currentDepth = ListUtil.newList() ; currentDepth.add(currentNode.fqn()) ; List<Fqn> result = ListUtil.newList() ; refMeChildren(result, currentDepth, refName, parent.children().toList()) ; return new ReadChildren(session, fqn, result.iterator()); } private void refMeChildren(List<Fqn> result, List<Fqn> currentDepth, String refName, List<ReadNode> children) { List<Fqn> newDepth = ListUtil.newList() ; for(ReadNode child : children){ for (Fqn cfqn : currentDepth) { if (child.hasRef(refName, cfqn)) { result.add(child.fqn()) ; newDepth.add(child.fqn()) ; break ; } } } if (newDepth.size() == 0) return ; refMeChildren(result, newDepth, refName, children) ; } public WalkReadChildren walkChildren() { return new WalkReadChildren(session, fqn, treeNode().getChildrenFqn().iterator()); } public <T> T toBean(Class<T> clz) { return transformer(Functions.beanCGIFunction(clz)); } public Rows toRows(String expr, FieldDefinition... fieldDefinitons) { return transformer(Functions.rowsFunction(session, expr, fieldDefinitons)); } public final static ReadNode ghost(ReadSession session, Fqn fqn) { return new GhostReadNode(session, fqn); } @Override public ChildQueryRequest childQuery(String query) throws IOException { if (StringUtil.isBlank(query)) return childQuery(new TermQuery(new Term(EntryKey.PARENT, this.fqn().toString()))); Central central = session.workspace().central(); Analyzer analyzer = central.searchConfig().queryAnalyzer(); try { final ChildQueryRequest result = ChildQueryRequest.create(session, session.newSearcher(), central.searchConfig().parseQuery(central.indexConfig(), analyzer, query)); result.filter(new TermFilter(EntryKey.PARENT, this.fqn().toString())); return result; } catch (ParseException e) { throw new IOException(e); } } public ChildQueryRequest childTermQuery(String name, String value, boolean includeDecentTree) throws IOException, ParseException { if (StringUtil.isBlank(name) || StringUtil.isBlank(value)) throw new ParseException(String.format("not defined name or value[%s:%s]", name, value)) ; final ChildQueryRequest result = ChildQueryRequest.create(session, session.newSearcher(), new TermQuery(new Term(name, value))); if (includeDecentTree){ result.filter(new QueryWrapperFilter(this.fqn().childrenQuery())); } else { result.filter(new TermFilter(EntryKey.PARENT, this.fqn().toString())); } return result; } public ChildQueryRequest childQuery(Query query) throws IOException { return ChildQueryRequest.create(session, session.newSearcher(), query); } public ChildQueryRequest childQuery(Query query, boolean includeDecentTree) throws IOException { if (!includeDecentTree) return childQuery(query); Analyzer analyzer = session().queryAnalyzer(); final ChildQueryRequest result = ChildQueryRequest.create(session, session.newSearcher(), query); result.filter(new QueryWrapperFilter(this.fqn().childrenQuery())); return result; } @Override public ChildQueryRequest childQuery(String query, boolean includeDecentTree) throws IOException { if (!includeDecentTree) return childQuery(query); if (StringUtil.isBlank(query)) return childQuery(this.fqn().childrenQuery()); try { Analyzer analyzer = session().queryAnalyzer(); Central central = session.workspace().central(); final ChildQueryRequest result = ChildQueryRequest.create(session, session.newSearcher(), central.searchConfig().parseQuery(central.indexConfig(), analyzer, query)); result.filter(new QueryWrapperFilter(this.fqn().childrenQuery())); return result; } catch (ParseException e) { throw new IOException(e); } } @Override public RefQueryRequest refsToMe(String refName) throws IOException { if (StringUtil.isBlank(refName)) throw new IllegalArgumentException("must required refName") ; Analyzer analyzer = session.workspace().central().searchConfig().queryAnalyzer(); final RefQueryRequest result = RefQueryRequest.createMe(session, session.newSearcher(), fqn(), refName); // result.filter(new TermFilter(EntryKey.PARENT, this.fqn().toString())); return result; } @Override public RefQueryRequest refsToChildren(String refName) throws IOException { if (StringUtil.isBlank(refName)) throw new IllegalArgumentException("must required refName") ; Analyzer analyzer = session.workspace().central().searchConfig().queryAnalyzer(); final RefQueryRequest result = RefQueryRequest.createChildren(session, session.newSearcher(), fqn(), refName); // result.filter(new TermFilter(EntryKey.PARENT, this.fqn().toString())); return result; } @Override public void template(String propId, Writer writer) throws IOException { Engine engine = session.workspace().parseEngine(); String template = property(propId).stringValue(); if (StringUtil.isBlank(template)) return; String result = engine.transform(template, MapUtil.<String, Object> create("self", this)); writer.write(result); } public JsonObject toValueJson() { JsonObject result = new JsonObject(); for (Entry<PropertyId, PropertyValue> prop : toMap().entrySet()) { result.add(prop.getKey().idString(), prop.getValue().json()); } return result ; } public boolean isGhost() { return false; } @Override public void debugPrint() { transformer(Functions.READ_DEBUGPRINT); } public boolean isMatch(String key, String value) throws IOException{ return this.property(key).defaultValue("").equals(session.encrypt(value)) ; } } class GhostReadNode extends ReadNodeImpl { private static final long serialVersionUID = -5073334525889136682L; private GhostTreeNode gnode ; GhostReadNode(ReadSession session, Fqn fqn) { super(session, fqn); this.gnode = new GhostTreeNode(session, fqn); } @Override public <T> T toBean(Class<T> clz) { return null; } public boolean isGhost() { return true; } @Override public Rows toRows(String expr, FieldDefinition... fieldDefinitons) { return AdNodeRows.create(session(), IteratorUtils.EMPTY_ITERATOR, expr, fieldDefinitons); // return FAKE ; } // only use for test public TreeNode<PropertyId, PropertyValue> treeNode() { return gnode ; } } class GhostTreeNode implements TreeNode<PropertyId, PropertyValue> { private ReadSession session; private Fqn fqn; GhostTreeNode(ReadSession session, Fqn fqn) { this.session = session; this.fqn = fqn ; } @Override public Set<TreeNode<PropertyId, PropertyValue>> getChildren() { return SetUtil.EMPTY; } @Override public Set<TreeNode<PropertyId, PropertyValue>> getChildren(Flag... flags) { return SetUtil.EMPTY; } @Override public Set<Object> getChildrenNames() { return SetUtil.EMPTY; } @Override public Set<Object> getChildrenNames(Flag... flags) { return SetUtil.EMPTY; } @Override public Map<PropertyId, PropertyValue> getData() { return MapUtil.EMPTY; } @Override public Map<PropertyId, PropertyValue> getData(Flag... flags) { return MapUtil.EMPTY; } @Override public Set<PropertyId> getKeys() { return SetUtil.EMPTY; } @Override public Set<PropertyId> getKeys(Flag... flags) { return SetUtil.EMPTY; } @Override public Fqn getFqn() { return fqn; } @Override public TreeNode<PropertyId, PropertyValue> addChild(Fqn f) { throw new UnsupportedOperationException("current node is ghost node"); } @Override public TreeNode<PropertyId, PropertyValue> addChild(Fqn f, Flag... flags) { throw new UnsupportedOperationException("current node is ghost node"); } @Override public boolean removeChild(Fqn f) { return false; } @Override public boolean removeChild(Fqn f, Flag... flags) { return false; } @Override public boolean removeChild(Object childName) { return false; } @Override public boolean removeChild(Object childName, Flag... flags) { return false; } @Override public TreeNode<PropertyId, PropertyValue> getChild(Fqn f) { throw new UnsupportedOperationException("current node is ghost node"); } @Override public TreeNode<PropertyId, PropertyValue> getChild(Fqn f, Flag... flags) { throw new UnsupportedOperationException("current node is ghost node"); } @Override public TreeNode<PropertyId, PropertyValue> getChild(Object name) { throw new UnsupportedOperationException("current node is ghost node"); } @Override public TreeNode<PropertyId, PropertyValue> getChild(Object name, Flag... flags) { throw new UnsupportedOperationException("current node is ghost node"); } @Override public PropertyValue put(PropertyId key, PropertyValue value) { throw new UnsupportedOperationException("current node is ghost node"); } @Override public PropertyValue put(PropertyId key, PropertyValue value, Flag... flags) { throw new UnsupportedOperationException("current node is ghost node"); } @Override public PropertyValue putIfAbsent(PropertyId key, PropertyValue value) { throw new UnsupportedOperationException("current node is ghost node"); } @Override public PropertyValue putIfAbsent(PropertyId key, PropertyValue value, Flag... flags) { throw new UnsupportedOperationException("current node is ghost node"); } @Override public PropertyValue replace(PropertyId key, PropertyValue value) { throw new UnsupportedOperationException("current node is ghost node"); } @Override public PropertyValue replace(PropertyId key, PropertyValue value, Flag... flags) { throw new UnsupportedOperationException("current node is ghost node"); } @Override public boolean replace(PropertyId key, PropertyValue oldValue, PropertyValue newValue) { throw new UnsupportedOperationException("current node is ghost node"); } @Override public boolean replace(PropertyId key, PropertyValue oldValue, PropertyValue newValue, Flag... flags) { throw new UnsupportedOperationException("current node is ghost node"); } @Override public void putAll(Map<? extends PropertyId, ? extends PropertyValue> map) { throw new UnsupportedOperationException("current node is ghost node"); } @Override public void putAll(Map<? extends PropertyId, ? extends PropertyValue> map, Flag... flags) { throw new UnsupportedOperationException("current node is ghost node"); } @Override public void replaceAll(Map<? extends PropertyId, ? extends PropertyValue> map) { throw new UnsupportedOperationException("current node is ghost node"); } @Override public void replaceAll(Map<? extends PropertyId, ? extends PropertyValue> map, Flag... flags) { throw new UnsupportedOperationException("current node is ghost node"); } @Override public PropertyValue get(PropertyId key) { return PropertyValue.NotFound; } @Override public PropertyValue get(PropertyId key, Flag... flags) { return PropertyValue.NotFound; } @Override public PropertyValue remove(PropertyId key) { return PropertyValue.NotFound; } @Override public PropertyValue remove(PropertyId key, Flag... flags) { return PropertyValue.NotFound; } @Override public void clearData() { } @Override public void clearData(Flag... flags) { } @Override public int dataSize() { return 0; } @Override public int dataSize(Flag... flags) { return 0; } @Override public boolean hasChild(Fqn f) { return false; } @Override public boolean hasChild(Fqn f, Flag... flags) { return false; } @Override public boolean hasChild(Object o) { return false; } @Override public boolean hasChild(Object o, Flag... flags) { return false; } @Override public boolean isValid() { return false; } @Override public void removeChildren() { } @Override public void removeChildren(Flag... flags) { } @Override public Set<Fqn> getChildrenFqn() { return SetUtil.EMPTY; } @Override public Set<Fqn> getReferencesFqn(String refName) { return SetUtil.EMPTY; } public boolean isProxyStatus(){ return false ; } public void proxyStatus(boolean b){ ; // } }