/** * PRISSMA is a presentation-level framework for Linked Data adaptation. * * Copyright (C) 2013 Luca Costabello, v1.0 * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, see <http://www.gnu.org/licenses/>. */ package fr.inria.wimmics.prissma.selection.utilities; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.hp.hpl.jena.rdf.model.Model; import com.hp.hpl.jena.rdf.model.ModelFactory; import com.hp.hpl.jena.rdf.model.Property; import com.hp.hpl.jena.rdf.model.RDFNode; import com.hp.hpl.jena.rdf.model.ResIterator; import com.hp.hpl.jena.rdf.model.Resource; import com.hp.hpl.jena.rdf.model.ResourceFactory; import com.hp.hpl.jena.rdf.model.SimpleSelector; import com.hp.hpl.jena.rdf.model.Statement; import com.hp.hpl.jena.rdf.model.StmtIterator; import fr.inria.wimmics.prissma.selection.Decomposer; import fr.inria.wimmics.prissma.selection.PrissmaProperties; import fr.inria.wimmics.prissma.selection.entities.ContextUnit; import fr.inria.wimmics.prissma.selection.entities.CtxUnitType; import fr.inria.wimmics.prissma.selection.entities.Decomposition; import fr.inria.wimmics.prissma.selection.entities.Edge; import fr.inria.wimmics.prissma.selection.exceptions.InputConversionException; public class ContextUnitConverter { public Set<ContextUnit> inputGraphContextUnits; public Set<Edge> inputGraphEdges; private Logger LOG = LoggerFactory.getLogger(ContextUnitConverter.class); // put here to check if a ctxunit is of type CLASS public Map<String, String> substitutions; public ContextUnitConverter(){ inputGraphContextUnits = new HashSet<ContextUnit>(); inputGraphEdges = new HashSet<Edge>(); } /** * Modifies the input RDFNode by substituting all Resources w/ their RDFS/OWL class. * Deletes the triples w/ property rdf:type from the model. * * The function is used to compact the decomposition, since it improves the chances * of collision, thus reducing fragmentation. * @param decomp * * @param RDFNode node */ public static RDFNode switchToClasses(RDFNode node, Decomposition decomp) { //FIXME should check if entity has children, and if entity is not TIME nor GEO // detects all the rdf:type/a properties, // associate the class to the instance in an external map // and delete the triple. decomp.substitutions = new HashMap<String, String>(); Model m = node.getModel(); StmtIterator it = m.listStatements( new SimpleSelector((Resource)null, PrissmaProperties.pType, (RDFNode) null) ); while (it.hasNext()){ Statement stmt = it.next(); Resource subj = stmt.getSubject(); Resource resClass = stmt.getObject().asResource(); decomp.substitutions.put(subj.getURI(), resClass.getURI()); it.remove(); } // substitute each element of the map // with its class everywhere in model for (String entityURI : decomp.substitutions.keySet()) { Resource classRes = ResourceFactory.createResource(decomp.substitutions.get(entityURI)); // subjects first SimpleSelector selSubj = new SimpleSelector(ResourceFactory.createResource(entityURI), (Property) null, (RDFNode) null) ; it = m.listStatements(selSubj); while (it.hasNext()){ Statement stmt = it.next(); Property p = stmt.getPredicate(); RDFNode obj = stmt.getObject(); m.add(classRes, p, obj); } m.remove(m.listStatements(selSubj)); // objects SimpleSelector selObj = new SimpleSelector((Resource) null, (Property) null, ResourceFactory.createResource(entityURI)) ; it = m.listStatements(selObj); while (it.hasNext()){ Statement stmt = it.next(); Property p = stmt.getPredicate(); Resource subj = stmt.getSubject(); m.add(subj, p, classRes); } m.remove(m.listStatements(selObj)); } return ContextUnitConverter.getRootCtxNode(m); } /** * Utility function. * Sets inputGraphContextUnits and inputGraphEdges. * @param gnode * @throws InputConversionException */ public void convertInputToUnits(RDFNode gnode, Collection<String> collection) { if (gnode == null){ LOG.info("input Ctx null. Assume test mode on. Skipping conversion."); return; } Model gNodeM = gnode.getModel(); Edge geoEdge = null, timeEdge = null; // it is already a simple ctxUnit if (gNodeM == null || gNodeM.isEmpty() ){ ContextUnit cu = null; if (gnode.isResource()){ if (collection.contains(gnode.asResource().getURI())) cu = new ContextUnit(CtxUnitType.CLASS); else cu = new ContextUnit(CtxUnitType.ENTITY); } else if (gnode.isLiteral()) cu = new ContextUnit(CtxUnitType.STRING); // TODO other datatypes cu.instance = gnode; inputGraphContextUnits.add(cu); return; } // contains complex ctxunit (GEO) else if (containsOnlyInternalProp(gnode, PrissmaProperties.internalGEOProperties)){ ContextUnit cu = new ContextUnit(CtxUnitType.GEO); cu.instance = gnode; inputGraphContextUnits.add(cu); } // contains complex ctxunit (TIME ) else if (containsOnlyInternalProp(gnode, PrissmaProperties.internalTIMEProperties)){ ContextUnit cu = new ContextUnit(CtxUnitType.TIME); cu.instance = gnode; inputGraphContextUnits.add(cu); } // if it's not a ctxUnit StmtIterator iter = gNodeM.listStatements(); Edge edge; // Storage for complex ctxUnits (GEO, TIME) RDFNode lat = null, lon = null, radius = null, start = null, duration = null, poi = null, time = null; ContextUnit cuGeo = null, cuTime = null; while (iter.hasNext()){ ContextUnit v2=null, v1=null; Statement stmt = iter.nextStatement(); Resource subject = stmt.getSubject(); Property predicate = stmt.getPredicate(); RDFNode object = stmt.getObject(); // check for GEO ctx unit type if (predicate.equals(PrissmaProperties.pPOI)){ v2 = new ContextUnit(CtxUnitType.GEO); cuGeo = v2; } // check for TIME ctx unit type else if (predicate.equals(PrissmaProperties.pTime)){ v2 = new ContextUnit(CtxUnitType.TIME); cuTime = v2; } else if (!Arrays.asList(PrissmaProperties.internalCtxUnitProperties).contains(predicate)){ // check if ENTITY type if (object.isResource()){ if (collection.contains(object.asResource().getURI())) v2 = new ContextUnit(CtxUnitType.CLASS); else v2 = new ContextUnit(CtxUnitType.ENTITY); } // check if STRING type //FIXME literal!=string else if (object.isLiteral()) v2 = new ContextUnit(CtxUnitType.STRING); } else{ if (predicate.equals(PrissmaProperties.pLat)) lat = object; else if (predicate.equals(PrissmaProperties.pLon)) lon = object; else if (predicate.equals(PrissmaProperties.pRad)) radius = object; else if (predicate.equals(PrissmaProperties.pStart)) start = object; else if (predicate.equals(PrissmaProperties.pDuration)) duration = object; } if (v2 != null) { // v1 is always ENTITY or CLASS type by construction if (collection.contains(subject.asResource().getURI())) v1 = new ContextUnit(CtxUnitType.CLASS); else v1 = new ContextUnit(CtxUnitType.ENTITY); v1.instance = subject; inputGraphContextUnits.add(v1); // duplicates are discarded if (v2.type != CtxUnitType.GEO && v2.type != CtxUnitType.TIME) { v2.instance = object; inputGraphContextUnits.add(v2); // duplicates are discarded } else if (v2.type == CtxUnitType.GEO) poi = object; else if (v2.type == CtxUnitType.TIME) time = object; // populate Edge and add it to list edge = new Edge(); ContextUnit v1Edge = new ContextUnit(v1.type); ContextUnit v2Edge = new ContextUnit(v2.type); Resource v1EdgeInstance = ResourceFactory.createResource(v1.instance.asResource().getURI()); // if v2 ctxunit if (v2.instance == null) edge.v2 = v2; else{ if (v2.instance.isResource()) v2Edge.instance = ResourceFactory.createResource(v2.instance.asResource().getURI()); else if (v2.instance.isLiteral()) v2Edge.instance = ResourceFactory.createPlainLiteral(v2.instance.asLiteral().getString()); edge.v2 = v2Edge; } v1Edge.instance = v1EdgeInstance; edge.v1 = v1Edge; edge.label = predicate; if (v2.type == CtxUnitType.GEO) geoEdge = edge; if (v2.type == CtxUnitType.TIME) timeEdge = edge; if (v2.type != CtxUnitType.GEO && v2.type != CtxUnitType.TIME) inputGraphEdges.add(edge); } } // Add GEO and TIME, if any. if (cuGeo != null){ Model m = ModelFactory.createDefaultModel(); if (lat != null) m.add(poi.asResource(), PrissmaProperties.pLat, lat); if (lon != null) m.add(poi.asResource(), PrissmaProperties.pLon, lon); if (radius != null) m.add(poi.asResource(), PrissmaProperties.pRad, radius); cuGeo.instance = getRoot(m, true); geoEdge.v2 = cuGeo; inputGraphEdges.add(geoEdge); inputGraphContextUnits.add(cuGeo); } if (cuTime != null) { Model m = ModelFactory.createDefaultModel(); if (start != null) m.add(time.asResource(), PrissmaProperties.pStart, start); if (duration != null) m.add(time.asResource(), PrissmaProperties.pDuration, duration); cuTime.instance = getRoot(m, true); timeEdge.v2 = cuTime; inputGraphEdges.add(timeEdge); inputGraphContextUnits.add(cuTime); } return; } private static boolean containsOnlyInternalProp(RDFNode g, Property[] properties) { Model mg = g.getModel(); if (mg == null) return false; // check geo and time StmtIterator it = mg.listStatements(); while (it.hasNext()){ Statement st = it.next(); Property p = st.getPredicate(); if (!Arrays.asList(properties).contains(p)) return false; } return true; // it contains only internalCtxUnitProperties (--> GEO or TIME ctxUnit) } /** * Return converted edge according to the desired jena Property. * @param prop * @return */ public Edge getEdge(Property prop) { Iterator<Edge> it = this.inputGraphEdges.iterator(); while(it.hasNext()){ Edge currentEdge = it.next(); if (currentEdge.label.equals(prop)) return currentEdge; } return null; } /** * Gets the root of a model (tree) and returns it in RDFNode object. * @param m * @param withProperties * @return */ public static RDFNode getRoot(Model m, boolean withProperties) { List<RDFNode> subjects = new ArrayList<RDFNode>(); List<RDFNode> objects = new ArrayList<RDFNode>(); StmtIterator it = m.listStatements(); while (it.hasNext()){ Statement st = it.next(); subjects.add(st.getSubject()); objects.add(st.getObject()); } for (RDFNode currSub : subjects) { if (!objects.contains(currSub)){ if (withProperties) return currSub; else{ RDFNode cleanCopy = createCleanCopy(currSub); return cleanCopy; } } } return null; } /** * create a copy of the RDFNode stripped out of the underlying model. * @param currSub * @return */ private static RDFNode createCleanCopy(RDFNode currSub) { RDFNode copy = null; if (currSub.isResource()){ copy = ResourceFactory.createResource(currSub.asResource().getURI()); } else if (currSub.isLiteral()){ copy = ResourceFactory.createPlainLiteral(currSub.asLiteral().getString()); } return copy; } public static boolean isCtxUnit(List<RDFNode> gList) { if (gList.size() > 1) return false; for (RDFNode g : gList) { if (! g.isLiteral() && !containsOnlyInternalProp(g, PrissmaProperties.internalCtxUnitProperties)) return false; } return true; } public static boolean isCtxUnit(RDFNode g) { if ( g.isLiteral() || isResourceLeaf(g) || containsOnlyInternalProp(g, PrissmaProperties.internalCtxUnitProperties) ) return true; return false; } private static boolean isResourceLeaf(RDFNode g) { if (g.isResource()){ if (g.getModel() == null) return true; StmtIterator it = g.asResource().listProperties(); while (it.hasNext()) return false; } return true; } public static boolean areIsomorphic(List<RDFNode> mItem, List<RDFNode> rootNodes) { int foundCnt = 0; for (RDFNode mItemNode : mItem) { for (RDFNode rdfNode : rootNodes) { if (areIsomorphic(mItemNode, rdfNode)) foundCnt ++; } } if (foundCnt == mItem.size()) return true; else return false; } public static boolean areIsomorphic(RDFNode g, RDFNode sMax) { // // if (gList.size() > 1) // return false; // for (RDFNode g : gList) { if (sMax == null) return false; Model mG = g.getModel(); Model mSMax = sMax.getModel(); // put empty models to null for simpler if clauses if (mG != null && mG.isEmpty()) mG = null; if (mSMax != null && mSMax.isEmpty()) mSMax = null; // both ctxUnit if ( (mG == null && mSMax == null) ){ if (!g.equals(sMax)) return false; // one of them is ctxunit } else if (mG != null && mSMax == null){ return false; } else if (mG == null && mSMax != null){ return false; } // not ctx units else{ if (!mG.getGraph().isIsomorphicWith(mSMax.getGraph())) return false; } // } return true; } public static RDFNode getRootCtxNode(Model prismModel) { RDFNode ctx = getSubjectForCoreProp(prismModel, PrissmaProperties.pUsr); if ( ctx != null) return ctx; ctx = getSubjectForCoreProp(prismModel, PrissmaProperties.pDev); if ( ctx != null) return ctx; ctx = getSubjectForCoreProp(prismModel, PrissmaProperties.pEnv); if ( ctx != null) return ctx; return null; } private static RDFNode getSubjectForCoreProp(Model prismModel, Property pCore) { ResIterator it = prismModel.listResourcesWithProperty(pCore); RDFNode ctx =null; while (it.hasNext()){ ctx = it.next(); } return ctx; } }