/* * Copyright 2013, TopicQuests * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions * and limitations under the License. */ package org.topicquests.topicmap.json.model; import java.util.*; import org.nex.util.DateUtil; import org.topicquests.common.ResultPojo; import org.topicquests.common.api.IResult; import org.topicquests.common.api.ITopicQuestsOntology; import org.topicquests.model.Node; import org.topicquests.model.TicketPojo; import org.topicquests.model.api.IMergeImplementation; import org.topicquests.model.api.node.IAddressableInformationResource; import org.topicquests.model.api.node.INode; import org.topicquests.model.api.node.INodeModel; import org.topicquests.model.api.node.ITuple; import org.topicquests.model.api.ITicket; import org.topicquests.topicmap.json.model.api.IJSONTopicDataProvider; import org.topicquests.util.LoggingPlatform; /** * @author park * */ public class NodeModel implements INodeModel { private JSONTopicmapEnvironment environment; private IJSONTopicDataProvider database; private IMergeImplementation merger; private Stack<INode>nodeStack; private int maxNumNodes = 100; private StatisticsUtility stats; private ITicket credentials; /** * */ public NodeModel(JSONTopicmapEnvironment env, IJSONTopicDataProvider db, IMergeImplementation m, int stackSize) { environment = env; database = db; stats = environment.getStats(); merger = m; nodeStack = new Stack<INode>(); maxNumNodes = stackSize; credentials = getDefaultCredentials(ITopicQuestsOntology.SYSTEM_USER); if (merger != null) merger.setNodeModel(this); } /** * Internal {@link INode} factory * @return */ private INode getNode() { // return new Node(); synchronized(nodeStack) { INode result = null; if (nodeStack.isEmpty()) result = new Node(); else result = nodeStack.pop(); return result; } } /* (non-Javadoc) * @see org.topicquests.model.api.INodeModel#newNode(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String, boolean) */ @Override public INode newNode(String locator, String label, String description, String lang, String userId, String smallImagePath, String largeImagePath, boolean isPrivate) { INode n = getNode(); // System.out.println("NodeModel.newNode- "+locator); //IResult result = new ResultPojo(); //result.setResultObject(n); n.setLocator(locator); n.setCreatorId(userId); n.setVersion(Long.toString(System.currentTimeMillis())); //default Date d = new Date(); String dateS = DateUtil.formatIso8601(d); n.setDate(dateS); n.setLastEditDate(dateS); if (label != null) n.addLabel(label, lang, userId, false); if (smallImagePath != null) n.setSmallImage(smallImagePath); if (largeImagePath != null) n.setImage(largeImagePath); if (description != null) n.addDetails(description, lang, userId, false); n.setIsPrivate(isPrivate); stats.addTopicNode(); // System.out.println("NodeModel.newNode+ "+locator); return n; } /* (non-Javadoc) * @see org.topicquests.model.api.INodeModel#newNode(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String, boolean) */ @Override public INode newNode(String label, String description, String lang, String userId, String smallImagePath, String largeImagePath, boolean isPrivate) { INode result = newNode(database.getUUID(),label,description,lang,userId,smallImagePath,largeImagePath,isPrivate); return result; } /* (non-Javadoc) * @see org.topicquests.model.api.INodeModel#newSubclassNode(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String, boolean) */ @Override public INode newSubclassNode(String locator, String superclassLocator, String label, String description, String lang, String userId, String smallImagePath, String largeImagePath, boolean isPrivate) { INode result = newNode(locator,label,description,lang,userId,smallImagePath,largeImagePath,isPrivate); result.addSuperclassId(superclassLocator); List<String>tc = _listTransitiveClosure(superclassLocator); result.setTransitiveClosure(tc); result.addTransitiveClosureLocator(superclassLocator); return result; } /** * Returns transitive closure for a given node * @param parentLocator * @return does not return <code>null</code> */ private List<String> _listTransitiveClosure(String parentLocator) { IResult x = database.getNode(parentLocator, credentials); INode n = (INode)x.getResultObject(); List<String>result = null; if (n != null) result = n.listTransitiveClosure(); if (result == null) result = new ArrayList<String>(); return result; } /* (non-Javadoc) * @see org.topicquests.model.api.INodeModel#newSubclassNode(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String, boolean) */ @Override public INode newSubclassNode(String superclassLocator, String label, String description, String lang, String userId, String smallImagePath, String largeImagePath, boolean isPrivate) { INode result = newNode(label,description,lang,userId,smallImagePath,largeImagePath,isPrivate); result.addSuperclassId(superclassLocator); List<String>tc = _listTransitiveClosure(superclassLocator); result.setTransitiveClosure(tc); result.addTransitiveClosureLocator(superclassLocator); return result; } /* (non-Javadoc) * @see org.topicquests.model.api.INodeModel#newInstanceNode(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String, boolean) */ @Override public INode newInstanceNode(String locator, String typeLocator, String label, String description, String lang, String userId, String smallImagePath, String largeImagePath, boolean isPrivate) { INode result = newNode(locator,label,description,lang,userId,smallImagePath,largeImagePath,isPrivate); result.setNodeType(typeLocator); List<String>tc = _listTransitiveClosure(typeLocator); result.setTransitiveClosure(tc); result.addTransitiveClosureLocator(typeLocator); return result; } /* (non-Javadoc) * @see org.topicquests.model.api.INodeModel#newInstanceNode(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String, boolean) */ @Override public INode newInstanceNode(String typeLocator, String label, String description, String lang, String userId, String smallImagePath, String largeImagePath, boolean isPrivate) { INode result = newNode(label,description,lang,userId,smallImagePath,largeImagePath,isPrivate); result.setNodeType(typeLocator); List<String>tc = _listTransitiveClosure(typeLocator); result.setTransitiveClosure(tc); result.addTransitiveClosureLocator(typeLocator); return result; } /* (non-Javadoc) * @see org.topicquests.model.api.INodeModel#updateNode(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String, boolean, java.util.Set) */ @Override public IResult updateNode(String nodeLocator, String updatedLabel, String updatedDetails, String language, String oldLabel, String oldDetails, String userId, boolean isLanguageAddition, ITicket credentials) { IResult result = database.getNode(nodeLocator, credentials); if (result.getResultObject() != null) { INode n = (INode)result.getResultObject(); if ((updatedLabel != null && !updatedLabel.equals("")) && (oldLabel == null || oldLabel.equals(""))) n.addLabel(updatedLabel, language, userId, isLanguageAddition); if ((updatedDetails != null && !updatedLabel.equals("")) && (oldDetails == null || oldLabel.equals(""))) n.addDetails(updatedDetails, language, userId, isLanguageAddition); List<String>val; String field; if (oldLabel != null) { field = n.makeField(ITopicQuestsOntology.LABEL_PROPERTY, language); val = (List<String>)n.getProperty(field); val.remove(oldLabel); val.add(updatedLabel); n.setProperty(field, val); } if (oldDetails != null) { field = n.makeField(ITopicQuestsOntology.DETAILS_PROPERTY, language); val = (List<String>)n.getProperty(field); val.remove(oldDetails); val.add(updatedDetails); n.setProperty(field, val); } return database.updateNode(n, true); } return result; } /* (non-Javadoc) * @see org.topicquests.model.api.INodeModel#changePropertyValue(org.topicquests.model.api.INode, java.lang.String, java.lang.String) */ @Override public IResult changePropertyValue(INode node, String key, String newValue) { IResult result = null; Object o = node.getProperty(key); if (o instanceof List) { //not a real case so we do nothing here result = new ResultPojo(); } else { String v = (String)o; if (!v.equals(newValue)) { node.setProperty(key, newValue); result = database.putNode(node, true); database.removeFromCache(node.getLocator()); } else result = new ResultPojo(); } return result; } /* (non-Javadoc) * @see org.topicquests.model.api.INodeModel#addPropertyValueInList(org.topicquests.model.api.INode, java.lang.String, java.lang.String) */ @Override public IResult addPropertyValueInList(INode node, String key, String newValue) { IResult result = null; String sourceNodeLocator = node.getLocator(); Map<String,Object> myMap = node.getProperties(); List<String>values = makeListIfNeeded( myMap.get(key)); if (!values.contains(newValue)) { values.add(newValue); result = database.putNode(node, true); database.removeFromCache(sourceNodeLocator); } else result = new ResultPojo(); return result; } /* (non-Javadoc) * @see org.topicquests.model.api.INodeModel#relateNodes(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String, boolean, boolean) */ @Override public IResult relateNodes(String sourceNodeLocator, String targetNodeLocator, String relationTypeLocator, String userId, String smallImagePath, String largeImagePath, boolean isTransclude, boolean isPrivate) { IResult result = new ResultPojo(); ITicket credentials = getDefaultCredentials(userId); //fetch the source actor node IResult x = database.getNode(sourceNodeLocator, credentials); INode nxA = (INode)x.getResultObject(); if (x.hasError()) result.addErrorString(x.getErrorString()); if (nxA != null) { //fetch target actor node IResult y = database.getNode(targetNodeLocator,credentials); INode nxB = (INode)y.getResultObject(); if (y.hasError()) result.addErrorString(y.getErrorString()); if (nxB != null) { y = relateExistingNodes(nxA, nxB, relationTypeLocator,userId,smallImagePath,largeImagePath,isTransclude,isPrivate); if (y.hasError()) result.addErrorString(y.getErrorString()); result.setResultObject(y.getResultObject()); } } return result; } /* (non-Javadoc) * @see org.topicquests.model.api.INodeModel#relateExistingNodes(org.topicquests.model.api.INode, org.topicquests.model.api.INode, java.lang.String, java.lang.String, java.lang.String, java.lang.String, boolean, boolean) */ @Override public IResult relateExistingNodes(INode sourceNode, INode targetNode, String relationTypeLocator, String userId, String smallImagePath, String largeImagePath, boolean isTransclude, boolean isPrivate) { IResult result = new ResultPojo(); String signature = sourceNode.getLocator()+relationTypeLocator+targetNode.getLocator(); //signature is the node's locator ITuple t = (ITuple)this.newInstanceNode(signature, relationTypeLocator, sourceNode.getLocator()+" "+relationTypeLocator+" "+targetNode.getLocator(), "en", userId, smallImagePath, largeImagePath, isPrivate); t.setIsTransclude(isTransclude); t.setObject(targetNode.getLocator()); t.setObjectType(ITopicQuestsOntology.NODE_TYPE); t.setSubjectLocator(sourceNode.getLocator()); t.setSubjectType(ITopicQuestsOntology.NODE_TYPE); String tlab = targetNode.getLabel("en"); if (tlab == null) { tlab = targetNode.getSubject("en"); } String slab = sourceNode.getLabel("en"); if (slab == null) { slab = sourceNode.getSubject("en"); } String tLoc = t.getLocator(); if (isPrivate) { sourceNode.addRestrictedRelation(relationTypeLocator,signature, relationTypeLocator,targetNode.getSmallImage(), targetNode.getLocator(), tlab, targetNode.getNodeType(), "t"); targetNode.addRestrictedRelation(relationTypeLocator,signature, relationTypeLocator,sourceNode.getSmallImage(), sourceNode.getLocator(), slab, sourceNode.getNodeType(), "s"); } else { sourceNode.addRelation(relationTypeLocator,signature,relationTypeLocator, targetNode.getSmallImage(), targetNode.getLocator(), tlab, targetNode.getNodeType(), "t"); targetNode.addRelation(relationTypeLocator,signature,relationTypeLocator, sourceNode.getSmallImage(), sourceNode.getLocator(), slab, sourceNode.getNodeType(), "s"); } IResult x = database.putNode(sourceNode, true); if (x.hasError()) result.addErrorString(x.getErrorString()); x = database.putNode(targetNode, true); if (x.hasError()) result.addErrorString(x.getErrorString()); database.putNode(t, true); if (x.hasError()) result.addErrorString(x.getErrorString()); //environment.logDebug("NodeModel.relateNewNodes "+sourceNode.getLocator()+" "+targetNode.getLocator()+" "+t.getLocator()+" | "+result.getErrorString()); //return the tuple itself result.setResultObject(t); return result; } /* (non-Javadoc) * @see org.topicquests.model.api.INodeModel#relateNewNodes(org.topicquests.model.api.INode, org.topicquests.model.api.INode, java.lang.String, java.lang.String, java.lang.String, java.lang.String, boolean, boolean) */ @Override public IResult relateNewNodes(INode sourceNode, INode targetNode, String relationTypeLocator, String userId, String smallImagePath, String largeImagePath, boolean isTransclude, boolean isPrivate) { IResult result = new ResultPojo(); String signature = sourceNode.getLocator()+relationTypeLocator+targetNode.getLocator(); ITuple t = (ITuple)this.newInstanceNode(relationTypeLocator, relationTypeLocator, sourceNode.getLocator()+" "+relationTypeLocator+" "+targetNode.getLocator(), "en", userId, smallImagePath, largeImagePath, isPrivate); t.setIsTransclude(isTransclude); t.setObject(targetNode.getLocator()); t.setObjectType(ITopicQuestsOntology.NODE_TYPE); t.setSubjectLocator(sourceNode.getLocator()); t.setSubjectType(ITopicQuestsOntology.NODE_TYPE); String tlab = targetNode.getLabel("en"); if (tlab == null) { tlab = targetNode.getSubject("en"); } String slab = sourceNode.getLabel("en"); if (slab == null) { slab = sourceNode.getSubject("en"); } String tLoc = t.getLocator(); if (isPrivate) { sourceNode.addRestrictedRelation(relationTypeLocator,signature, relationTypeLocator,targetNode.getSmallImage(), targetNode.getLocator(), tlab, targetNode.getNodeType(), "t"); targetNode.addRestrictedRelation(relationTypeLocator,signature, relationTypeLocator,sourceNode.getSmallImage(), sourceNode.getLocator(), slab, sourceNode.getNodeType(), "s"); } else { sourceNode.addRelation(relationTypeLocator, signature, relationTypeLocator,targetNode.getSmallImage(), targetNode.getLocator(), tlab, targetNode.getNodeType(), "t"); targetNode.addRelation(relationTypeLocator, signature,relationTypeLocator,sourceNode.getSmallImage(), sourceNode.getLocator(), slab, sourceNode.getNodeType(), "s"); } IResult x = database.putNode(sourceNode, true); if (x.hasError()) result.addErrorString(x.getErrorString()); x = database.putNode(targetNode, true); if (x.hasError()) result.addErrorString(x.getErrorString()); database.putNode(t, true); if (x.hasError()) result.addErrorString(x.getErrorString()); //environment.logDebug("NodeModel.relateNewNodes "+sourceNode.getLocator()+" "+targetNode.getLocator()+" "+t.getLocator()+" | "+result.getErrorString()); result.setResultObject(t); return result; } @Override public IResult relateExistingNodesAsPivots(INode sourceNode, INode targetNode, String relationTypeLocator, String userId, String smallImagePath, String largeImagePath, boolean isTransclude, boolean isPrivate) { IResult result = new ResultPojo(); String signature = sourceNode.getLocator()+relationTypeLocator+targetNode.getLocator(); ITuple t = (ITuple)this.newInstanceNode(signature, relationTypeLocator, sourceNode.getLocator()+" "+relationTypeLocator+" "+targetNode.getLocator(), "en", userId, smallImagePath, largeImagePath, isPrivate); t.setIsTransclude(isTransclude); t.setObject(targetNode.getLocator()); t.setObjectType(ITopicQuestsOntology.NODE_TYPE); t.setSubjectLocator(sourceNode.getLocator()); t.setSubjectType(ITopicQuestsOntology.NODE_TYPE); String tlab = targetNode.getLabel("en"); if (tlab == null) { tlab = targetNode.getSubject("en"); } String slab = sourceNode.getLabel("en"); if (slab == null) { slab = sourceNode.getSubject("en"); } String tLoc = t.getLocator(); if (isPrivate) { sourceNode.addRestrictedPivot(relationTypeLocator,signature,relationTypeLocator, targetNode.getSmallImage(), targetNode.getLocator(), tlab, targetNode.getNodeType(), "t"); targetNode.addRestrictedPivot(relationTypeLocator,signature, relationTypeLocator, sourceNode.getSmallImage(), sourceNode.getLocator(), slab, sourceNode.getNodeType(), "s"); } else { sourceNode.addPivot(relationTypeLocator,signature, relationTypeLocator,targetNode.getSmallImage(), targetNode.getLocator(), tlab, targetNode.getNodeType(), "t"); targetNode.addPivot(relationTypeLocator,signature, relationTypeLocator,sourceNode.getSmallImage(), sourceNode.getLocator(), slab, sourceNode.getNodeType(), "s"); } /////////////////////////////////////////// //TODO // WE are now OptimisticLockException sensitive // And it might be possible that we need the // ability to detect that /////////////////////////////////////////// IResult x = database.putNode(sourceNode,true); if (x.hasError()) result.addErrorString(x.getErrorString()); x = database.putNode(targetNode,true); if (x.hasError()) result.addErrorString(x.getErrorString()); database.putNode(t,true); if (x.hasError()) result.addErrorString(x.getErrorString()); //environment.logDebug("NodeModel.relateExistingNodesAsPivots "+sourceNode.getLocator()+" "+targetNode.getLocator()+" "+t.getLocator()+" | "+result.getErrorString()); result.setResultObject(t); return result; } /* (non-Javadoc) * @see org.topicquests.model.api.INodeModel#assertMerge(java.lang.String, java.lang.String, java.util.Map, double, java.lang.String) */ @Override public IResult assertMerge(String sourceNodeLocator, String targetNodeLocator, Map<String, Double> mergeData, double mergeConfidence, String userLocator) { IResult result = new ResultPojo(); // TODO Auto-generated method stub return result; } /* (non-Javadoc) * @see org.topicquests.model.api.INodeModel#assertPossibleMerge(java.lang.String, java.lang.String, java.util.Map, double, java.lang.String) */ @Override public IResult assertPossibleMerge(String sourceNodeLocator, String targetNodeLocator, Map<String, Double> mergeData, double mergeConfidence, String userLocator) { IResult result = new ResultPojo(); // TODO Auto-generated method stub return result; } /* (non-Javadoc) * @see org.topicquests.model.api.INodeModel#assertUnmerge(java.lang.String, org.topicquests.model.api.INode, java.util.Map, double, java.lang.String) */ @Override public IResult assertUnmerge(String sourceNodeLocator, INode targetNodeLocator, Map<String, Double> mergeData, double mergeConfidence, String userLocator) { IResult result = new ResultPojo(); // TODO Auto-generated method stub return result; } /* (non-Javadoc) * @see org.topicquests.model.api.INodeModel#removeNode(java.lang.String) */ @Override public IResult removeNode(String locator) { IResult result = new ResultPojo(); // TODO Auto-generated method stub return result; } /* (non-Javadoc) * @see org.topicquests.model.api.INodeModel#getDefaultCredentials(java.lang.String) */ @Override public ITicket getDefaultCredentials(String userId) { ITicket result = new TicketPojo (userId); return result; } /** * * @param o * @return can return <code>null</code> */ List<String> makeListIfNeeded(Object o) { if (o == null) return null; List<String>result = null; if (o instanceof List) result = (List<String>)o; else { result = new ArrayList<String>(); result.add((String)o); } return result; } @Override public void recycleNode(INode node) { // node = null; database.removeFromCache(node.getLocator()); synchronized(nodeStack) { if (nodeStack.size() >= this.maxNumNodes) node = null; else { node.getProperties().clear(); nodeStack.push(node); } } } /** @Override public IAddressableInformationResource newAIR(String locator, String subject, String body, String language, String userId, boolean isPrivate) { IAddressableInformationResource result = (IAddressableInformationResource)getNode(); String lox = locator; if (lox == null) lox = database.getUUID(); result.setLocator(lox); result.setNodeType(ITopicQuestsOntology.AIR_TYPE); List<String>tc = _listTransitiveClosure(ITopicQuestsOntology.AIR_TYPE); result.setTransitiveClosure(tc); result.addTransitiveClosureLocator(ITopicQuestsOntology.AIR_TYPE); result.setCreatorId(userId); result.setSubject(subject, language); result.setBody(body,language); Date d = new Date(); result.setDate(d); result.setLastEditDate(d); return result; } */ @Override public IResult addSuperClass(INode node, String superClassLocator) { node.addSuperclassId(superClassLocator); IResult result = database.getNode(superClassLocator, credentials); INode s = (INode)result.getResultObject(); result.setResultObject(null); List<String>stc = s.listTransitiveClosure(); List<String>ntc = node.listTransitiveClosure(); if (ntc == null) ntc = new ArrayList<String>(); ntc.add(superClassLocator); if (stc != null && !stc.isEmpty()) { String x; for (int i=0;i<stc.size();i++) { x = stc.get(i); if (!ntc.contains(x)) ntc.add(x); } } result = database.putNode(node, true); return result; } @Override public IResult setNodeType(INode node, String typeLocator) { //NOTE: if this node was already a type, it just got wiped out node.setNodeType(typeLocator); IResult result = database.getNode(typeLocator, credentials); INode s = (INode)result.getResultObject(); result.setResultObject(null); List<String>stc = s.listTransitiveClosure(); List<String>ntc = node.listTransitiveClosure(); if (ntc == null) ntc = new ArrayList<String>(); ntc.add(typeLocator); if (stc != null && !stc.isEmpty()) { String x; for (int i=0;i<stc.size();i++) { x = stc.get(i); if (!ntc.contains(x)) ntc.add(x); } } result = database.putNode(node, true); return result; } }