/*
* Copyright (c) 2015 Data Harmonisation Panel
*
* All rights reserved. This program and the accompanying materials are made
* available under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of the License,
* or (at your option) any later version.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution. If not, see <http://www.gnu.org/licenses/>.
*
* Contributors:
* Data Harmonisation Panel <http://www.dhpanel.eu>
*/
package eu.esdihumboldt.hale.common.instance.graph.reference.impl;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import javax.xml.XMLConstants;
import javax.xml.namespace.QName;
import eu.esdihumboldt.hale.common.instance.graph.reference.IdentityReferenceInspector;
import eu.esdihumboldt.hale.common.instance.helper.DepthFirstInstanceTraverser;
import eu.esdihumboldt.hale.common.instance.helper.InstanceTraversalCallback;
import eu.esdihumboldt.hale.common.instance.model.Group;
import eu.esdihumboldt.hale.common.instance.model.Instance;
import eu.esdihumboldt.hale.common.schema.model.ChildDefinition;
import eu.esdihumboldt.hale.common.schema.model.DefinitionGroup;
import eu.esdihumboldt.hale.common.schema.model.constraint.property.Reference;
/**
* Identity and reference inspector for XML IDs and local XLink references.
*
* @author Simon Templer
*/
public class XMLInspector implements IdentityReferenceInspector<String> {
/**
* Collects local XLink references.
*/
public class LocalXLinks implements InstanceTraversalCallback {
private final Set<String> referencedIDs = new HashSet<>();
@Override
public boolean visit(Instance instance, QName name, DefinitionGroup parent) {
return true;
}
@Override
public boolean visit(Group group, QName name, DefinitionGroup parent) {
return true;
}
@Override
public boolean visit(Object value, QName name, DefinitionGroup parent) {
if (value != null && parent != null && "href".equals(name.getLocalPart())) {
ChildDefinition<?> def = parent.getChild(name);
if (def != null && def.asProperty() != null) {
Reference ref = def.asProperty().getConstraint(Reference.class);
if (ref.isReference()) {
String idRef = ref.extractId(value).toString();
if (!idRef.contains("#") && !idRef.startsWith("http://")
&& !idRef.startsWith("https://")) {
// an extracted local reference will not contain #
// also have to filter out by protocol because there
// can be code list references being detected as
// references (e.g. INSPIRE code lists)
referencedIDs.add(idRef);
}
}
}
}
return true;
}
/**
* @return the referenced identifiers
*/
public Set<String> getReferencedIDs() {
return Collections.unmodifiableSet(referencedIDs);
}
}
private static final QName ID_TYPE_NAME = new QName(XMLConstants.W3C_XML_SCHEMA_NS_URI, "ID");
@Override
public String getIdentity(Instance instance) {
// look for XML ID attributes in top level properties
// return the first ID found
for (QName property : instance.getPropertyNames()) {
ChildDefinition<?> def = instance.getDefinition().getChild(property);
if (def != null && def.asProperty() != null) {
if (ID_TYPE_NAME.equals(def.asProperty().getPropertyType().getName())) {
Object[] values = instance.getProperty(property);
if (values != null && values.length > 0 && values[0] != null) {
return values[0].toString();
}
}
}
}
return null;
}
@Override
public Set<String> getReferencedIdentities(Instance instance) {
// find all (local) xlink references
DepthFirstInstanceTraverser traverser = new DepthFirstInstanceTraverser();
LocalXLinks xlinks = new LocalXLinks();
traverser.traverse(instance, xlinks);
return xlinks.getReferencedIDs();
}
}