/* * Created on Mar 16, 2005 */ package edu.mit.simile.fresnel.configuration; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.Vector; import javax.xml.parsers.ParserConfigurationException; import org.openrdf.model.Literal; import org.openrdf.model.Resource; import org.openrdf.model.Statement; import org.openrdf.model.URI; import org.openrdf.model.Value; import org.openrdf.model.impl.URIImpl; import org.openrdf.repository.Repository; import org.openrdf.repository.RepositoryConnection; import org.openrdf.repository.RepositoryException; import org.openrdf.repository.RepositoryResult; import fr.inria.jfresnel.fsl.FSLNSResolver; import edu.mit.simile.fresnel.facets.Facet; import edu.mit.simile.fresnel.facets.FacetSet; import edu.mit.simile.fresnel.format.ClassFormat; import edu.mit.simile.fresnel.format.Format; import edu.mit.simile.fresnel.format.InstanceFormat; import edu.mit.simile.fresnel.purpose.Purpose; import edu.mit.simile.fresnel.results.ResultConstants; import edu.mit.simile.fresnel.results.Selection; import edu.mit.simile.fresnel.selection.ISelector; import edu.mit.simile.fresnel.selection.InstanceLens; import edu.mit.simile.fresnel.selection.InstanceSelector; import edu.mit.simile.fresnel.selection.InvalidResultSetException; import edu.mit.simile.fresnel.selection.Lens; import edu.mit.simile.fresnel.selection.ParsingException; import edu.mit.simile.fresnel.selection.ResourceNotFoundException; import edu.mit.simile.fresnel.selection.TypeSelector; import edu.mit.simile.fresnel.selection.UnresolvableException; import edu.mit.simile.vocabularies.Facets; import edu.mit.simile.vocabularies.FresnelCore; import edu.mit.simile.vocabularies.Namespaces; /** * Takes a Fresnel configuration RDF model and makes it useful for * selecting and styling other RDF models. * * @author ryanlee */ public class Configuration implements ResultConstants { /** * URI for the default group, if no groups exist in the configuration */ private final String DEFAULT_GROUP_URI = "urn:simile.mit.edu:fresnel:defaultgroup"; /** * Resource for default group URI */ private final Resource DEFAULT_GROUP = new URIImpl(DEFAULT_GROUP_URI); /** * URI for the default group, if no groups exist in the configuration */ private final String DEFAULT_FORMAT_URI = "urn:simile.mit.edu:fresnel:defaultformat"; /** * Resource for default group URI */ private final Resource DEFAULT_FORMAT = new URIImpl(DEFAULT_FORMAT_URI); /** * Original RDF model of the configuration */ private Repository _source; /** * RDF model containing ontology information such as subclass and label relationships. */ private Repository _ontologies; /** * List of lenses */ private Set<Lens> _lenses; /** * List of styles */ private Set<Format> _formats; /** * EXTENSION hash of all facet sets */ private FacetSetHashMap _facets; /** * List of lens groups */ private GroupHashMap _groups; /** * Set of groups in increasing string order */ private Set<Resource> _groupOrder; /** * List of lens to resource matches */ private LensMatchHashMap _lensMatches; /** * Hash of class domain lenses based on the instance */ private LensMatchHashMap _instanceLensMatches; /** * Hash of class domain lenses based on the domain */ private LensMatchHashMap _classLensMatches; /** * Hash of resource format matches to resources based on the domain */ private HashMap<Resource, ResourceFormatMatchSet> _formatResourceMatches; /** * Hash of property format matches based on the domain */ private HashMap<Resource, PropertyFormatMatchSet> _formatPropertyMatches; /** * Group used in selection, should also be used for formatting. */ private Group _mainGroup; /** * Hash of everything by config model resource */ private HashMap<Resource, Object> _all; /** * Gathers warnings for reporting to the user */ private WarningGatherer _warnings; /** * Map of namespaces to abbreviations for FSL. */ private FSLNSResolver _nsmap; /** * String of namespaces for SPARQL queries. */ private String _namespaces; /** * Constructor, initializer based on RDF graph of configuration; needs other data, * cannot be called on its own. */ private Configuration(Repository source) throws ParsingException, UnresolvableException { this._source = source; this._lenses = new HashSet<Lens>(); this._formats = new HashSet<Format>(); this._facets = new FacetSetHashMap(); this._groupOrder = new HashSet<Resource>(); // TODO: find a way to set initial capacity correctly this._groups = new GroupHashMap(); this._lensMatches = new LensMatchHashMap(); this._instanceLensMatches = new LensMatchHashMap(); this._classLensMatches = new LensMatchHashMap(); this._formatResourceMatches = new HashMap<Resource, ResourceFormatMatchSet>(); this._formatPropertyMatches = new HashMap<Resource, PropertyFormatMatchSet>(); this._all = new HashMap<Resource, Object>(); this._warnings = new WarningGatherer(); this._nsmap = new FSLNSResolver(); this.parse(); } /** * Constructs a Configuration object based on an RDF model of configuration and a model * of ontology data. * * @param source The source <code>Model</code> * @param ontologies The ontology source <code>Model</code> * @throws ParsingException If errors are encountered in the configuration semantics that * do not conform to the Fresnel ontology * @throws UnresolvableException If errors are encountered in the number of options made * available for certain properties, leading to a choice this * code is not qualified to make */ public Configuration(Repository source, Repository ontologies) throws ParsingException, UnresolvableException { this(source); this._ontologies = ontologies; } /** * Internal method to accrue warnings * * @param warning An <code>Exception</code> to gather */ private void gather(Exception warning) { this._warnings.addWarning(warning); } /** * Whether any warnings were accrued * * @return True if warnings were encountered, false if not */ public boolean hasWarnings() { return (this._warnings.size() > 0); } /** * Retrieve the warnings as a string message * * @return A <code>String</code> of accrued warnings. */ public String getWarningsString() { return this._warnings.toString(); } /** * Parses a model into the Configuration's internal structure, based on data supplied * to the constructor. Heavy lifter. * * @throws ParsingException If errors are encountered in the configuration semantics that * do not conform to the Fresnel ontology * @throws UnresolvableException If errors are encountered in the number of options made * available for certain properties, leading to a choice this * code is not qualified to make */ private void parse() throws ParsingException, UnresolvableException { try { RepositoryConnection conn = this._source.getConnection(); // EXTENSION for FSL namespace mapping - not part of core Fresnel vocabulary // Namespaces for SPARQL are put into a String StringBuffer namespaces = new StringBuffer(); RepositoryResult<Statement> it = conn.getStatements((Resource) null, Namespaces.abbreviated, (Value) null, false); while (it.hasNext()) { Statement nsStatement = (Statement) it.next(); URI ns = (URI) nsStatement.getSubject(); Literal abbrev = (Literal) nsStatement.getObject(); this._nsmap.addPrefixBinding(abbrev.getLabel(), ns.toString()); namespaces.append("prefix "); namespaces.append(abbrev.getLabel() + ":"); namespaces.append("<" + ns.toString() + ">"); } it.close(); this._namespaces = namespaces.toString(); it = conn.getStatements((Resource) null, FresnelCore.instanceLensDomain, (Value) null, false); while (it.hasNext()) { try { Statement lensResourceStatement = (Statement) it.next(); InstanceLens lens = new InstanceLens(this._source, lensResourceStatement.getSubject(), this); addInstanceLens(lens); if (!this._all.containsKey(lens.getIdentifier())) addLens(lens); } catch (ParsingException e) { gather(e); } catch (UnresolvableException u) { gather(u); } } it.close(); it = conn.getStatements((Resource) null, FresnelCore.classLensDomain, (Value) null, false); while (it.hasNext()) { try { Statement lensResourceStatement = (Statement) it.next(); Lens lens = new Lens(this._source, lensResourceStatement.getSubject(), this); if (!this._all.containsKey(lens.getIdentifier())) addLens(lens); } catch (ParsingException e) { gather(e); } catch (UnresolvableException u) { gather(u); } } it.close(); it = conn.getStatements((Resource) null, FresnelCore.propertyFormatDomain, (Value) null, false); while (it.hasNext()) { try { Statement formatResourceStatement = (Statement) it.next(); Format format = Format.parse(this._source, formatResourceStatement.getSubject(), this); if (!this._all.containsKey(format.getIdentifier())) addPropertyFormat(format); } catch (ParsingException e) { gather(e); } catch (UnresolvableException u) { gather(u); } } it.close(); it = conn.getStatements((Resource) null, FresnelCore.instanceFormatDomain, (Value) null, false); while (it.hasNext()) { try { Statement formatResourceStatement = (Statement) it.next(); InstanceFormat format = (InstanceFormat) InstanceFormat.parse(this._source, formatResourceStatement.getSubject(), this); if (!this._all.containsKey(format.getIdentifier())) addInstanceFormat(format); } catch (ParsingException e) { gather(e); } catch (UnresolvableException u) { gather(u); } } it.close(); it = conn.getStatements((Resource) null, FresnelCore.classFormatDomain, (Value) null, false); while (it.hasNext()) { try { Statement formatResourceStatement = (Statement) it.next(); ClassFormat format = (ClassFormat) ClassFormat.parse(this._source, formatResourceStatement.getSubject(), this); if (!this._all.containsKey(format.getIdentifier())) addClassFormat(format); } catch (ParsingException e) { gather(e); } catch (UnresolvableException u) { gather(u); } } it.close(); // EXTENSION for declaring facets - not part of core Fresnel vocabulary this._facets.clear(); it = conn.getStatements((Resource) null, Facets.facets, (Value) null, false); while (it.hasNext()) { try { Statement facetStatement = (Statement) it.next(); FacetSet facetset = FacetSet.parse(this._source, facetStatement.getSubject()); if (!this._all.containsKey(facetset.getIdentifier())) addFacetSet(facetset); } catch (Exception e) { gather(e); } } it.close(); // also catch even if it only has to do with hiding it = conn.getStatements((Resource) null, Facets.hides, (Value) null, false); while (it.hasNext()) { try { Statement facetStatement = (Statement) it.next(); FacetSet facetset = FacetSet.parse(this._source, facetStatement.getSubject()); if (!this._all.containsKey(facetset.getIdentifier())) addFacetSet(facetset); } catch (Exception e) { gather(e); } } it.close(); // get a list of unique resources that are a group by direct inference it = conn.getStatements((Resource) null, FresnelCore.group, (Value) null, false); while (it.hasNext()) { try { Statement groupStatement = (Statement) it.next(); Resource groupMember = groupStatement.getSubject(); Resource groupResource = null; Value groupValue = groupStatement.getObject(); if (groupValue instanceof Resource) groupResource = (Resource) groupValue; else throw new UnresolvableException("Group referred to is not a resource: " + groupValue); boolean exists = this._groups.containsKey(groupResource); Group group = exists ? (Group) this._groups.get(groupResource) : Group.parse(this._source, groupResource, this); try { group.addLens(lensLookup(groupMember)); } catch (ResourceNotFoundException e) { try { group.addFormat(formatLookup(groupMember)); } catch (ResourceNotFoundException re) { throw new ParsingException("Could not find useful parsing information for resource: " + ((URI) groupMember).toString()); } } // Group only needs to be parsed once, don't add it if it already exists if (!exists) { addGroup(group); } } catch (ParsingException e) { gather(e); } catch (UnresolvableException u) { gather(u); } } it.close(); conn.close(); } catch (RepositoryException e) { throw new UnresolvableException("Problem connecting to repository: " + e.getLocalizedMessage()); } } /** * Internal method for adding instance lenses consistently. * * @param lens <code>InstanceLens</code> to add to configuration */ private void addInstanceLens(Lens lens) { this._lenses.add(lens); this._all.put(lens.getIdentifier(), lens); for (Iterator<ISelector> di = lens.getDomainSet().iterator(); di.hasNext(); ) { ISelector domain = di.next(); // TODO other valid selector types may need to be integrated, such as FSE/SPARQL selectors if (domain instanceof InstanceSelector) { InstanceSelector is = (InstanceSelector) domain; LensMatchSet lms = this._instanceLensMatches.getMatch(is.getInstance()); if (null == lms) { lms = new LensMatchSet(is.getInstance()); } lms.add(lens); this._instanceLensMatches.putMatch(is.getInstance(), lms); } } } /** * Internal method for adding lenses consistently. * * @param lens <code>Lens</code> to add to configuration. */ private void addLens(Lens lens) { this._lenses.add(lens); this._all.put(lens.getIdentifier(), lens); for (Iterator<ISelector> di = lens.getDomainSet().iterator(); di.hasNext(); ) { TypeSelector ts = (TypeSelector) di.next(); LensMatchSet lms = this._classLensMatches.getMatch(ts.getType()); if (null == lms) { lms = new LensMatchSet(ts.getType()); } lms.add(lens); this._classLensMatches.putMatch(ts.getType(), lms); } } /** * Internal method for adding formats consistently. * * @param style <code>Format</code> to add to configuration. */ private void addPropertyFormat(Format format) { this._formats.add(format); this._all.put(format.getIdentifier(), format); } /** * Internal method for adding instance formats consistently. * * @param style <code>Format</code> to add to configuration. */ private void addInstanceFormat(Format format) { this._formats.add(format); this._all.put(format.getIdentifier(), format); } /** * Internal method for adding class formats consistently. * * @param style <code>Format</code> to add to configuration. */ private void addClassFormat(Format format) { this._formats.add(format); this._all.put(format.getIdentifier(), format); } /** * EXTENSION for declaring facets * * @param fs A set of facets */ private void addFacetSet(FacetSet fs) { if (fs.isForAll()) this._facets.setDefaultSet(fs); else { for (Iterator<Resource> ti = fs.getForTypes(); ti.hasNext(); ) { this._facets.putFacetSet(ti.next(), fs); } } this._all.put(fs.getIdentifier(), fs); } /** * Internal method for adding groups consistently. * * @param group <code>Group</code> to add to configuration. */ private void addGroup(Group group) { this._groups.put(group.getIdentifier(), group); this._groupOrder.add(group.getIdentifier()); this._all.put(group.getIdentifier(), group); } /** * Selects based on lens matches, outputs an intermediate XML tree. * * @param in The data <code>Repository</code> * @return A subgraph <code>Selection</code> * @throws ParserConfigurationException When a problem with the XML parsing code is encountered * @throws NoResultsException When no results can be generated from the given configuration and data */ public Selection select(Repository in, String langPref) throws NoResultsException, ParserConfigurationException { Selection answer = null; Group group = null; if (this._groups.size() > 0) { // this just takes the first group if it's undefined; // a smarter way to do it would be to do some fast analysis on which // group matches the most resources for (Iterator<Resource> groupIt = this._groupOrder.iterator(); groupIt.hasNext(); ) { Resource groupRes = (Resource) groupIt.next(); Group groupCandidate = this._groups.getGroup(groupRes); if (groupCandidate.getLenses().size() > 0) { group = groupCandidate; break; } } } if (null == group) { // put everything in a default group if no groups containing lenses exists group = new Group(DEFAULT_GROUP); for (Lens l : this._lenses) { group.addLens(l); } for (Format f : this._formats) { group.addFormat(f); } } answer = select(in, group, langPref); return answer; } /** * Selects based on lens matches and a group of interest, outputs an intermediate XML tree. * * @param in The input data <code>Model</code> * @param grouping The <code>Group</code> of lenses and styles to use * @return A subgraph <code>Selection</code> * @throws ParserConfigurationException When a problem with the XML parsing code is encountered * @throws NoResultsException When no results can be generated from the given configuration and data */ public Selection select(Repository in, Group grouping, String langPref) throws ParserConfigurationException, NoResultsException { this._mainGroup = grouping; // immediate exception if no lenses exist if (this._lenses.size() == 0) throw new NoResultsException("There are no lenses defined!"); // execute every lens for pre-selection, make a lensMatch for every resource // that gets selected. keep adding to the lensMatch per resource until all // matches are found (all lenses are executed). Vector<Resource> validStarts = new Vector<Resource>(); if (grouping.hasPrimaries()) { Iterator<ISelector> primariesIt = grouping.getPrimaries().iterator(); while (primariesIt.hasNext()) { ISelector primary = primariesIt.next(); if (primary.canSelectResources()) { try { Iterator<Resource> valids = primary.selectResources(in); while (valids.hasNext()) { validStarts.add(valids.next()); } } catch (InvalidResultSetException e) { // not a valid exception at this point } } } } else { validStarts = null; } Iterator<Lens> li = grouping.getLenses().iterator(); while (li.hasNext()) { Lens lens = li.next(); Iterator<ISelector> di = lens.getDomainSet().iterator(); while (di.hasNext()) { ISelector select = di.next(); if (select.canSelectResources()) { try { Iterator<Resource> ri = select.selectResources(in); while (ri.hasNext()) { Resource res = (Resource) ri.next(); if (null == validStarts || (null != validStarts && validStarts.contains(res))) { if (this._lensMatches.containsKey(res)) { LensMatchSet match = this._lensMatches.getMatch(res); match.add(lens); } else { LensMatchSet match = new LensMatchSet(res); match.add(lens); this._lensMatches.putMatch(res, match); } } } } catch (InvalidResultSetException e) { // } } } } // exception if no matches if (this._lensMatches.size() == 0) throw new NoResultsException("No lenses matching the data could be found."); Selection answer = new Selection(this); answer.setLangPref(langPref); Iterator<?> resources = this._lensMatches.keySet().iterator(); while (resources.hasNext()) { Resource subject = (Resource) resources.next(); LensMatchSet match = this._lensMatches.getMatch(subject); Lens best = match.topMatch(); answer.addPrimary(answer.applyLens(grouping, in, best, subject, 0, MAXIMUM_LENS_DEPTH)); } return answer; } /** * Select specific resource based on lens matches, outputs a Fresnel-specific Selection. * * @param in The data <code>Repository</code> * @param focus The specific <code>Resource</code> to use Fresnel on * @return A subgraph <code>Selection</code> * @throws ParserConfigurationException When a problem with the XML parsing code is encountered * @throws NoResultsException When no results can be generated from the given configuration and data */ public Selection select(Repository in, Resource focus, String langPref) throws NoResultsException, ParserConfigurationException { Selection answer = null; Group group = null; if (this._groups.size() > 0) { // this just takes the first group if it's undefined; // a smarter way to do it would be to do some fast analysis on which // group matches the most resources for (Iterator<Resource> groupIt = this._groupOrder.iterator(); groupIt.hasNext(); ) { Resource groupRes = (Resource) groupIt.next(); Group groupCandidate = this._groups.getGroup(groupRes); if (groupCandidate.getLenses().size() > 0) { group = groupCandidate; break; } } } if (null == group) { // put everything in a default group if no groups containing lenses exists group = new Group(DEFAULT_GROUP); for (Lens l : this._lenses) { group.addLens(l); } for (Format f : this._formats) { group.addFormat(f); } } answer = select(in, focus, group, langPref); return answer; } /** * Internal selection mechanism, returns matches. */ private LensMatchSet _select(Repository in, Resource focus, Group grouping) throws ParserConfigurationException, NoResultsException { this._mainGroup = grouping; // immediate exception if no lenses exist if (this._lenses.size() == 0) throw new NoResultsException("There are no lenses defined!"); // execute every lens for pre-selection, make a lensMatch for every resource // that gets selected. keep adding to the lensMatch per resource until all // matches are found (all lenses are executed). Vector<Resource> validStarts = new Vector<Resource>(); if (grouping.hasPrimaries()) { Iterator<ISelector> primariesIt = grouping.getPrimaries().iterator(); while (primariesIt.hasNext()) { ISelector primary = primariesIt.next(); if (primary.canSelectResources()) { try { Iterator<Resource> valids = primary.selectResources(in); while (valids.hasNext()) { validStarts.add(valids.next()); } } catch (InvalidResultSetException e) { // not a valid exception at this point } } } } else { validStarts = null; } LensMatchSet match = null; Iterator<Lens> li = grouping.getLenses().iterator(); while (li.hasNext()) { Lens lens = li.next(); Iterator<ISelector> di = lens.getDomainSet().iterator(); while (di.hasNext()) { ISelector select = di.next(); if (select.canSelectResources()) { if (select.canSelect(in, focus)) { if (null == validStarts || (null != validStarts && validStarts.contains(focus))) { if (this._lensMatches.containsKey(focus)) { match = this._lensMatches.getMatch(focus); match.add(lens); } else { match = new LensMatchSet(focus); match.add(lens); this._lensMatches.putMatch(focus, match); } } } } } } return match; } /** * Selects based on lens matches and a group of interest and a lens purpose, outputs an intermediate XML tree. * * @param in The input data <code>Model</code> * @param focus The specific <code>Resource</code> to use Fresnel on * @param grouping The <code>Group</code> of lenses and styles to use * @param purpose The <code>Purpose</code> of the lens * @return A subgraph <code>Selection</code> * @throws ParserConfigurationException When a problem with the XML parsing code is encountered * @throws NoResultsException When no results can be generated from the given configuration and data */ public Selection select(Repository in, Resource focus, Group grouping, Purpose purpose, String langPref) throws ParserConfigurationException, NoResultsException { if (null == purpose) return select(in, focus, grouping, langPref); LensMatchSet match = _select(in, focus, grouping); // exception if no matches if (null == match) throw new NoResultsException("No lenses matching the data could be found."); Selection answer = new Selection(this); answer.setLangPref(langPref); Lens best = match.topMatch(); // check purposes for (Iterator<Lens> pli = match.lensIterator(); pli.hasNext(); ) { Lens potential = pli.next(); if (potential.hasPurpose(purpose)) { best = potential; break; } } answer.addPrimary(answer.applyLens(grouping, in, best, focus, 0, MAXIMUM_LENS_DEPTH)); return answer; } /** * Selects based on lens matches and a group of interest and a lens purpose, outputs an intermediate XML tree. * * @param in The input data <code>Model</code> * @param focus The specific <code>Resource</code> to use Fresnel on * @param grouping The <code>Group</code> of lenses and styles to use * @param purpose The <code>Purpose</code> of the lens * @return A subgraph <code>Selection</code> * @throws ParserConfigurationException When a problem with the XML parsing code is encountered * @throws NoResultsException When no results can be generated from the given configuration and data */ public Selection select(Repository in, Resource focus, Purpose purpose, String langPref) throws ParserConfigurationException, NoResultsException { if (null == purpose) return select(in, focus, langPref); Group group = null; if (this._groups.size() > 0) { // this just takes the first group if it's undefined; // a smarter way to do it would be to do some fast analysis on which // group matches the most resources for (Iterator<Resource> groupIt = this._groupOrder.iterator(); groupIt.hasNext(); ) { Resource groupRes = (Resource) groupIt.next(); Group groupCandidate = this._groups.getGroup(groupRes); if (groupCandidate.getLenses().size() > 0) { group = groupCandidate; break; } } } if (null == group) { // put everything in a default group if no groups containing lenses exists group = new Group(DEFAULT_GROUP); for (Lens l : this._lenses) { group.addLens(l); } for (Format f : this._formats) { group.addFormat(f); } } return select(in, focus, group, purpose, langPref); } /** * Selects based on lens matches and a group of interest, outputs an intermediate XML tree. * * @param in The input data <code>Model</code> * @param focus The specific <code>Resource</code> to use Fresnel on * @param grouping The <code>Group</code> of lenses and styles to use * @return A subgraph <code>Selection</code> * @throws ParserConfigurationException When a problem with the XML parsing code is encountered * @throws NoResultsException When no results can be generated from the given configuration and data */ public Selection select(Repository in, Resource focus, Group grouping, String langPref) throws ParserConfigurationException, NoResultsException { LensMatchSet match = _select(in, focus, grouping); // exception if no matches if (null == match) throw new NoResultsException("No lenses matching the data could be found."); Selection answer = new Selection(this); answer.setLangPref(langPref); Lens best = match.topMatch(); answer.addPrimary(answer.applyLens(grouping, in, best, focus, 0, MAXIMUM_LENS_DEPTH)); return answer; } /** * Formats the subgraph selected by a select() call according to Fresnel formatting * configuration. * * @param in The data <code>Repository</code> * @param select The previously generated <code>Selection</code> * @return A <code>Selection</code> with formatting information. */ public Selection format(Repository in, Selection select) { Group grouping = this._mainGroup; // RESOURCES // execute every format for pre-selection, make a formatMatch for every resource // that gets selected. keep adding to the lensMatch per resource until all // matches are found (all formats are executed). Iterator<Format> fi = grouping.getFormats().iterator(); Format defaultFormat = new Format(DEFAULT_FORMAT); while (fi.hasNext()) { Format format = fi.next(); Iterator<ISelector> di = format.getDomainSet().iterator(); while (di.hasNext()) { ISelector selects = di.next(); if (selects.canSelectResources()) { /* * Only ClassFormat and InstanceFormat select resources, * so the vast amount of property formats can be skipped */ if (!(format instanceof ClassFormat || format instanceof InstanceFormat)) continue; try { Iterator<Resource> ri = selects.selectResources(in); while (ri.hasNext()) { Resource res = ri.next(); if (this._formatResourceMatches.containsKey(res)) { ResourceFormatMatchSet match = this._formatResourceMatches.get(res); match.addClassFormat(format); } else { ResourceFormatMatchSet match = new ResourceFormatMatchSet(res); match.addClassFormat(format); this._formatResourceMatches.put(res, match); } } } catch (InvalidResultSetException e) { // TODO } } else if (selects.canSelectStatements()) { // inefficient - should really ask the model via sparql or something // about all the predicates it contains try { Iterator<Statement> si = selects.selectStatements(select.getModel(), null); while (si.hasNext()) { URI prop = si.next().getPredicate(); if (this._formatPropertyMatches.containsKey(prop)) { PropertyFormatMatchSet match = this._formatPropertyMatches.get(prop); match.addPropertyFormat(format); } else { PropertyFormatMatchSet match = new PropertyFormatMatchSet(prop); match.addPropertyFormat(format); this._formatPropertyMatches.put(prop, match); } } si = selects.selectStatements(select.getNotModel(), null); while (si.hasNext()) { URI prop = si.next().getPredicate(); if (this._formatPropertyMatches.containsKey(prop)) { PropertyFormatMatchSet match = this._formatPropertyMatches.get(prop); match.addPropertyFormat(format); } else { PropertyFormatMatchSet match = new PropertyFormatMatchSet(prop); match.addPropertyFormat(format); this._formatPropertyMatches.put(prop, match); } } } catch (InvalidResultSetException e) { ; } } } } // er...in retrospect, subjects is the wrong word - it's any resource, be it subject or // object with no predicates of its own Iterator<?> subjects = select.getResourceHash().keySet().iterator(); while (subjects.hasNext()) { Resource subject = (Resource) subjects.next(); ResourceFormatMatchSet match = this._formatResourceMatches.get(subject); select.applyFormat(grouping, (null != match) ? match.topMatch() : defaultFormat, subject, Selection.RESOURCE, in); } // must go through *all* the properties! // some class of stuff says this property would have been selected if it existed (not sure how // this goes with FSL...), then when formatting comes along, fill it in with contentNoValue if // a Format exists; if not, show nothing Iterator<?> properties = select.getPropertyHash().keySet().iterator(); while (properties.hasNext()) { URI subject = (URI) properties.next(); PropertyFormatMatchSet match = this._formatPropertyMatches.get(subject); select.applyFormat(grouping, (null != match) ? match.topMatch() : defaultFormat, subject, Selection.PROPERTY, in); } return select; } /** * Find an existing lens for re-use in another context outside straight parsing, * such as sublens parsing; parse and add the lens if not found. * * @param identifier The <code>Resource</code> identifying the lens * @return The found <code>Lens</code> * @throws ParsingException If the lens is not found anywhere or if there are problems parsing the lens * @throws UnresolvableException If there are problems parsing the lens */ public Lens lensLookup(Resource identifier) throws ParsingException, UnresolvableException, ResourceNotFoundException { Lens out = null; if (this._all.containsKey(identifier) && this._lenses.contains(this._all.get(identifier))) out = (Lens) this._all.get(identifier); else { RepositoryConnection conn = null; try { conn = this._source.getConnection(); if (conn.hasStatement(identifier, FresnelCore.instanceLensDomain, (Value) null, false)) { out = new InstanceLens(this._source, identifier, this); addInstanceLens(out); } else if (conn.hasStatement(identifier, FresnelCore.classLensDomain, (Value) null, false)) { out = new Lens(this._source, identifier, this); addLens(out); } else { throw new ResourceNotFoundException("Explicitly named lens not found in configuration"); } } catch (RepositoryException e) { throw new UnresolvableException("Problem connecting to repository: " + e.getLocalizedMessage()); } finally { try { conn.close(); } catch (RepositoryException e) { } } } return out; } /** * Lookup an existing group or parse a new one into the configuration * * @param identifier The <code>Resource</code> identifier * @return A <code>Group</code> * @throws ParsingException If a parsing error occurs in creating a new group * @throws UnresolvableException If an unresolvable error occurs in creating a new group */ public Group groupLookupOrAdd(Resource identifier) throws ParsingException, UnresolvableException, ResourceNotFoundException { Group out = null; if (this._all.containsKey(identifier) && this._groups.containsKey(identifier)) out = (Group) this._all.get(identifier); else { RepositoryConnection conn = null; try { conn = this._source.getConnection(); if (conn.hasStatement((Resource) null, FresnelCore.group, (Value) identifier, false)) { out = Group.parse(this._source, identifier, this); addGroup(out); } else { throw new ResourceNotFoundException("Explicitly named group not found in configuration"); } } catch (RepositoryException e) { throw new UnresolvableException("Problems connecting to repository: " + e.getLocalizedMessage()); } finally { if (conn != null) try { conn.close(); } catch (RepositoryException e) { e.printStackTrace(); } } } return out; } /** * Only looks up the group identifier, returns null if no group found. * * @param identifier A <code>Resource</code> * @return The <code>Group</code> or null if not found */ public Group groupLookup(Resource identifier) { Group out = null; if (this._all.containsKey(identifier) && this._groups.containsKey(identifier)) out = (Group) this._all.get(identifier); return out; } /** * Retreives the group currently used by default * * @return A <code>Group</code> */ public Group getCurrentGroup() { Group group = this._mainGroup; if (null == group) { if (this._groups.size() > 0) { // this just takes the first group if it's undefined; // a smarter way to do it would be to do some fast analysis on which // group matches the most resources for (Iterator<Resource> groupIt = this._groupOrder.iterator(); groupIt.hasNext(); ) { Resource groupRes = (Resource) groupIt.next(); Group groupCandidate = this._groups.getGroup(groupRes); if (groupCandidate.getLenses().size() > 0) { group = groupCandidate; break; } } } if (null == group) { // put everything in a default group if no groups containing lenses exists group = new Group(DEFAULT_GROUP); } } return group; } /** * Lookup an existing format or parse it into configuration if it does not exist. * * @param identifier The <code>Format</code> identifier to lookup * @return A <code>Format</code> * @throws ParsingException If there are parsing errors in making a new format * @throws UnresolvableException If there are unresolvable errors in making a new format */ public Format formatLookup(Resource identifier) throws ParsingException, UnresolvableException, ResourceNotFoundException { Format out = null; if (this._all.containsKey(identifier) && this._formats.contains(this._all.get(identifier))) out = (Format) this._all.get(identifier); else { try { RepositoryConnection conn = this._source.getConnection(); if (conn.hasStatement(identifier, FresnelCore.propertyFormatDomain, (Value) null, false)) { out = Format.parse(this._source, identifier, this); addPropertyFormat(out); } else if (conn.hasStatement(identifier, FresnelCore.instanceFormatDomain, (Value) null, false)) { out = InstanceFormat.parse(this._source, identifier, this); addInstanceFormat(out); } else if (conn.hasStatement(identifier, FresnelCore.classFormatDomain, (Value) null, false)) { out = ClassFormat.parse(this._source, identifier, this); addClassFormat(out); } else { throw new ResourceNotFoundException("Explicitly named style not found in configuration"); } conn.close(); } catch (RepositoryException e) { throw new UnresolvableException("Problem connecting to repository: " + e.getLocalizedMessage()); } } return out; } /** * Fetch all the groups in the configuration, probably for UI purposes * * @return A <code>Vector</code> of configured group values */ public Vector<Group> groups() { return new Vector<Group>(this._groups.values()); } /** * EXTENSION method for finding facets per class * * @param forClasses A <code>List</code> of <code>Resource</code>s * @return A <code>List</code> of <code>Resource</code>s */ public List<Resource> facets(Set<?> forClasses) { Vector<Resource> out = new Vector<Resource>(); if (this._facets.hasDefaultSet()) { for (Iterator<Facet> fi = this._facets.getDefaultSet().facetIterator(); fi.hasNext(); ) { out.add(fi.next().getIdentifier()); } } if (null != forClasses) { for (Iterator<?> it = forClasses.iterator(); it.hasNext(); ) { Iterator<FacetSet> fsi = this._facets.getFacetSetIterator((Resource) it.next()); if (null != fsi) { while (fsi.hasNext()) { for (Iterator<Facet> fi = fsi.next().facetIterator(); fi.hasNext(); ) { Facet nextF = fi.next(); if (!out.contains(nextF)) out.add(nextF.getIdentifier()); } } } } } return out; } /** * EXTENSION method for finding hidden facets per class * * @param forClasses A <code>List</code> of <code>Resource</code>s * @return A <code>List</code> of <code>Resource</code>s */ public List<Resource> hiddenFacets(Set<?> forClasses) { Vector<Resource> out = new Vector<Resource>(); if (this._facets.hasDefaultSet()) { for (Iterator<Facet> fi = this._facets.getDefaultSet().hideIterator(); fi.hasNext(); ) { out.add(fi.next().getIdentifier()); } } if (null != forClasses) { for (Iterator<?> it = forClasses.iterator(); it.hasNext(); ) { Iterator<FacetSet> fsi = this._facets.getFacetSetIterator((Resource) it.next()); if (null != fsi) { while (fsi.hasNext()) { for (Iterator<Facet> fi = fsi.next().hideIterator(); fi.hasNext(); ) { Facet nextF = fi.next(); if (!out.contains(nextF)) out.add(nextF.getIdentifier()); } } } } } return out; } /** * Get the parsed namespace / abbreviation map for FSL. * * @return An <code>FSLNSResolver</code> */ public FSLNSResolver getNamespaceMap() { return this._nsmap; } /** * Get the namespaces of the configuration for SPARQL queries. * * @return A Set<Namespace> set. */ public String getNamespaces() { return this._namespaces; } /** * Retrieve the hash of all lens and resource matches. * * @return A <code>LensMatchHashMap</code> */ public LensMatchHashMap getLensMatches() { return this._lensMatches; } /** * Retreive the hash of all instance lens and resource matches. * * @return A <code>LensMatchHashMap</code> */ public LensMatchHashMap getInstanceLensMatches() { return this._instanceLensMatches; } /** * Retreive the hash of all class lens and resource matches. * * @return A <code>LensMatchHashMap</code> */ public LensMatchHashMap getClassLensMatches() { return this._classLensMatches; } /** * Queries the internal ontology graph for a set of labels for a resource based on a given * labelling property. * * @param subject The subject <code>Resource</code> to find labels for * @param labelPropertyURI The property <code>URI</code> to find objects of * @return A <code>StatementIterator</code> with statements whose objects are labels of the subject */ public Iterator<Statement> getOntologyLabels(Resource subject, URI labelPropertyURI) { if (this._ontologies != null) { // TODO: problematic Vector<Statement> out = new Vector<Statement>(); try { RepositoryConnection conn = this._ontologies.getConnection(); RepositoryResult<Statement> it = conn.getStatements(subject, labelPropertyURI, (Value) null, false); while (it.hasNext()) { out.add(it.next()); } it.close(); conn.close(); } catch (RepositoryException e) { // TODO: how to handle exception } return out.iterator(); } else { return null; } } /** * @see java.lang.Object#toString() */ public String toString() { String state = super.toString() + "\n"; state += " Lens count: " + this._lenses.size() + "\n"; state += " Group count: " + this._groups.size() + "\n"; state += " Format count: " + this._formats.size() + "\n"; state += " Facet count: " + this._facets.size() + "\n"; Iterator<Lens> lensIt = this._lenses.iterator(); Iterator<Group> groupIt = this._groups.values().iterator(); Iterator<Format> styleIt = this._formats.iterator(); state += "\n[Lenses]\n"; while (lensIt.hasNext()) { state += "\n" + lensIt.next() + "\n"; } state += "\n[Groups]\n"; while (groupIt.hasNext()) { state += "\n" + groupIt.next() + "\n"; } state += "\n[Formats]\n"; while (styleIt.hasNext()) { state += "\n" + styleIt.next() + "\n"; } state += "\n[Facets]\n"; state += "\n" + this._facets.toString() + "\n"; return state; } }