/* See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* Esri Inc. licenses this file to You 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 com.esri.gpt.catalog.discovery;
import com.esri.gpt.catalog.lucene.Storeables;
import com.esri.gpt.framework.collection.CaseInsensitiveMap;
import com.esri.gpt.framework.collection.StringSet;
import com.esri.gpt.framework.geometry.Envelope;
import com.esri.gpt.framework.util.Val;
import java.util.Map;
import java.util.logging.Logger;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
* Contains the configured collection of discoverble/storable property meanings.
*/
@SuppressWarnings("serial")
public class PropertyMeanings extends CaseInsensitiveMap<PropertyMeaning> {
/** class variables ========================================================= */
/** The Logger. */
private static Logger LOGGER = Logger.getLogger(PropertyMeanings.class.getName());
/** Date modified property name = "dateModified" */
public static final String NAME_DATEMODIFIED = "dateModified";
/** Document UUID (primary key) name = "uuid" */
public static final String NAME_UUID = "uuid";
/** instance variables ====================================================== */
private AliasedDiscoverables allAliased = new AliasedDiscoverables();
private PropertySets dcPropertySets = new PropertySets();
private IStoreables storeables = new Storeables();
/** constructors ============================================================ */
/** Default constructor. */
public PropertyMeanings() {
super(false);
}
/** properties ============================================================== */
/**
* Gets the entire map of aliased discoverables.
* @return the map of aliased discoverables
*/
public AliasedDiscoverables getAllAliased() {
return allAliased;
}
/**
* Gets the configured Dublin Core property sets.
* @return the property sets
*/
public PropertySets getDcPropertySets() {
return dcPropertySets;
}
/** methods ================================================================= */
/**
* Adds a property meaning to the collection.
* @param meaning the property meaning to add
*/
private void add(PropertyMeaning meaning) {
if (meaning != null) {
put(meaning.getName(),meaning);
}
}
/**
* Loads property meanings from an XML document.
* <p/>
* The XML document is based upon the configuration file:<br/>
* gpt/metadata/property-meanings.xml
* @param dom the XML document associated with the property meanings configuration file
* @throws XPathExpressionException if an invalid XPath expression was encountered
*/
public void load(Document dom) throws XPathExpressionException {
this.clear();
XPath xpath = XPathFactory.newInstance().newXPath();
Node root = (Node)xpath.evaluate("/property-meanings",dom,XPathConstants.NODE);
if (root != null) {
NodeList nlProps = (NodeList)xpath.evaluate("property-meaning",root,XPathConstants.NODESET);
String name;
PropertyMeaning meaning;
PropertyMeaningType meaningType;
PropertyValueType valueType;
PropertyComparisonType compType;
// default allow leading wildcard setting
String alwExpr = "/property-meanings/property-meaning[@name='anytext']/@allowLeadingWildcard";
boolean alwDefault = Val.chkStr(xpath.evaluate(alwExpr,dom)).equalsIgnoreCase("true");
// loop through the properties
for (int i=0;i<nlProps.getLength();i++) {
Node ndProp = nlProps.item(i);
name = Val.chkStr(xpath.evaluate("@name",ndProp));
meaningType = PropertyMeaningType.from(xpath.evaluate("@meaningType",ndProp));
valueType = PropertyValueType.from(xpath.evaluate("@valueType",ndProp));
compType = PropertyComparisonType.from(xpath.evaluate("@comparisonType",ndProp));
meaning = new PropertyMeaning(name,meaningType,valueType,compType);
if (meaning.getMeaningType().equals(PropertyMeaningType.ANYTEXT)) {
meaning = new PropertyMeaning.AnyText();
String names = xpath.evaluate("consider/text()",ndProp);
((PropertyMeaning.AnyText)meaning).getNamesToConsider().addDelimited(names);
} else if (meaning.getMeaningType().equals(PropertyMeaningType.GEOMETRY)) {
meaning = new PropertyMeaning.Geometry();
Envelope envelope = null;
String tokens = xpath.evaluate("defaultEnvelope/text()",ndProp);
String[] coords = Val.tokenize(tokens," ");
if (coords.length == 4) {
envelope = new Envelope();
envelope.put(coords[0],coords[1],coords[2],coords[3]);
if (envelope.isValid()) {
((PropertyMeaning.Geometry)meaning).setDefaultEnvelope(envelope);
}
}
}
meaning.setAllowLeadingWildcard(
Val.chkBool(xpath.evaluate("@allowLeadingWildcard",ndProp),alwDefault));
this.add(meaning);
NodeList nlChildren = ndProp.getChildNodes();
for (int j=0;j<nlChildren.getLength();j++) {
Node ndChild = nlChildren.item(j);
if (ndChild.getNodeType() == Node.ELEMENT_NODE) {
String qName = Val.chkStr(xpath.evaluate("@name",ndChild));
StringSet qAliases = new StringSet();
if (qName.length() > 0) {
// Dublin Core elements
if (ndChild.getNodeName().equalsIgnoreCase("dc")) {
DcElement dc = new DcElement(qName);
dc.getAliases().add(dc.getElementName());
dc.getAliases().addDelimited(xpath.evaluate("@aliases",ndChild));
dc.setScheme(xpath.evaluate("@scheme",ndChild));
meaning.setDcElement(dc);
qAliases = dc.getAliases();
// other elements
} else {
qAliases.add(qName);
qAliases.addDelimited(xpath.evaluate("@aliases",ndChild));
}
// append to the all aliased discoverables collection
IStoreable storeable = storeables.connect(meaning);
if (storeable != null) {
Discoverable discoverable = new Discoverable(qName);
discoverable.setMeaning(meaning);
discoverable.setStoreable(storeable);
for (String alias: qAliases) {
this.getAllAliased().put(alias,discoverable);
}
this.getAllAliased().put(meaning.getName(),discoverable);
}
}
}
}
}
}
// load explicit aliases
if (root != null) {
NodeList nl = (NodeList)xpath.evaluate("property-alias",root,XPathConstants.NODESET);
for (int i=0;i<nl.getLength();i++) {
Node nd = nl.item(i);
String name = Val.chkStr(xpath.evaluate("@meaning-name",nd));
String value = Val.chkStr(xpath.evaluate("@value",nd));
PropertyMeaning meaning = this.get(name);
if (meaning == null) {
LOGGER.warning("property-meanings.xml/property-alias@meaning-name="+name+" is invalid.");
} else {
Discoverable discoverable = this.getAllAliased().get(name);
if (discoverable != null) {
StringSet aliases = new StringSet();
aliases.addDelimited(value);
for (String alias: aliases) {
this.getAllAliased().put(alias,discoverable);
}
}
}
}
}
// make the Dublin Core aliased map
AliasedDiscoverables dcAliased = dcPropertySets.getAllAliased();
for (PropertyMeaning meaning: values()) {
DcElement dcElement = meaning.getDcElement();
if ((dcElement != null) && (dcElement.getElementName().length() > 0)) {
IStoreable storeable = storeables.connect(meaning);
if (storeable != null) {
Discoverable discoverable = new Discoverable(dcElement.getElementName());
discoverable.setMeaning(meaning);
discoverable.setStoreable(storeable);
StringSet aliases = dcElement.getAliases();
for (String alias: aliases) {
dcAliased.put(alias,discoverable);
}
dcAliased.put(meaning.getName(),discoverable);
}
}
}
// parse the property sets
if (root != null) {
Node ndSets = (Node)xpath.evaluate("propertySets",root,XPathConstants.NODE);
if (ndSets != null) {
Node ndBrief = (Node)xpath.evaluate("brief",ndSets,XPathConstants.NODE);
Node ndSummary = (Node)xpath.evaluate("summary",ndSets,XPathConstants.NODE);
Node ndFull = (Node)xpath.evaluate("full",ndSets,XPathConstants.NODE);
loadDcPropertySet(ndBrief,xpath,dcAliased,dcPropertySets.getBrief());
loadDcPropertySet(ndSummary,xpath,dcAliased,dcPropertySets.getSummary());
loadDcPropertySet(ndFull,xpath,dcAliased,dcPropertySets.getFull());
}
}
}
/**
* Loads a Dublin core property set.
* @param parent the parant node for the set
* @param xpath and XPath that can be used for dom queries
* @param aliased the full set of aliased discoverables
* @param discoverables the collection of discoverables to populate
* @throws XPathExpressionException if an invalid XPath expression was encountered
*/
private void loadDcPropertySet(Node parent,
XPath xpath,
AliasedDiscoverables aliased,
Discoverables discoverables)
throws XPathExpressionException {
StringSet names = new StringSet();
if (parent != null) {
Node ndDc = (Node)xpath.evaluate("dc",parent,XPathConstants.NODE);
if (ndDc != null) {
NodeList nlNames = (NodeList)xpath.evaluate("meaning-names",ndDc,XPathConstants.NODESET);
for (int i=0;i<nlNames.getLength();i++) {
names.addDelimited(nlNames.item(i).getTextContent());
}
}
for (String name: names) {
Discoverable discoverable = aliased.get(name);
if ((discoverable != null) && (discoverable.getMeaning().getDcElement() != null)) {
String dcName = Val.chkStr(discoverable.getMeaning().getDcElement().getElementName());
if (dcName.length() > 0) {
discoverables.add(aliased.get(name));
}
}
}
}
}
/**
* Appends property information for the component to a buffer.
* @param sb the buffer to use when appending information
*/
public void echo(StringBuffer sb) {
if (size() == 0) {
sb.append(" (No meanings.)");
} else {
for (Map.Entry<String,PropertyMeaning> entry: this.entrySet()) {
sb.append("\nkey=\"").append(entry.getKey()).append("\"\n");
((PropertyMeaning)entry.getValue()).echo(sb,1);
}
}
}
/**
* Returns the string representation of the object.
* @return the string
*/
@Override
public String toString() {
StringBuffer sb = new StringBuffer(getClass().getName()).append(" (");
echo(sb);
sb.append("\n) ===== end ").append(getClass().getName());
return sb.toString();
}
}