/* * Geotoolkit - An Open Source Java GIS Toolkit * http://www.geotoolkit.org * * (C) 2010, Geomatys * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library 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 * Lesser General Public License for more details. */ package org.geotoolkit.filter.binding; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.NoSuchElementException; import org.jaxen.FunctionCallException; import org.jaxen.JaxenConstants; import org.jaxen.Navigator; import org.jaxen.UnsupportedAxisException; import org.jaxen.XPath; import org.jaxen.saxpath.SAXPathException; import org.jaxen.util.AncestorAxisIterator; import org.jaxen.util.AncestorOrSelfAxisIterator; import org.jaxen.util.DescendantAxisIterator; import org.jaxen.util.DescendantOrSelfAxisIterator; import org.jaxen.util.FollowingAxisIterator; import org.jaxen.util.FollowingSiblingAxisIterator; import org.jaxen.util.PrecedingAxisIterator; import org.jaxen.util.PrecedingSiblingAxisIterator; import org.jaxen.util.SelfAxisIterator; import org.geotoolkit.util.NamesExt; import org.opengis.feature.Attribute; import org.opengis.feature.Feature; import org.opengis.feature.FeatureAssociation; import org.opengis.feature.FeatureAssociationRole; import org.opengis.feature.FeatureType; import org.opengis.feature.Property; import org.opengis.feature.PropertyType; import org.opengis.filter.identity.Identifier; import org.opengis.util.GenericName; /** * xpath navigator for features. * * Element == ComplexType * Attribut == Property * * @author Johann Sorel (Geomatys) * @module */ final class JaxenFeatureNavigator implements Navigator{ private static final String EMPTY = ""; @Override public String getElementNamespaceUri(final Object o) { if(o instanceof Fake){ final Fake candidate = (Fake) o; return getNamespace(candidate.name); }else if(o instanceof Feature){ final Feature candidate = (Feature) o; return getNamespace(candidate.getType().getName()); }else if(o instanceof Property){ final Property candidate = (Property) o; return getNamespace(candidate.getName()); }else if(o instanceof PropertyType){ final PropertyType candidate = (PropertyType) o; return getNamespace(candidate.getName()); } return null; } private String getNamespace(GenericName candidate){ final String ns = NamesExt.getNamespace(candidate); if(ns==null || ns.isEmpty()){ return null; }else{ return ns; } } @Override public String getElementName(final Object o) { String str = null; if(o instanceof Fake){ final Fake candidate = (Fake) o; str = candidate.name.tip().toString(); }else if(o instanceof Feature){ final Feature candidate = (Feature) o; str = candidate.getType().getName().tip().toString(); }else if(o instanceof Property){ final Property candidate = (Property) o; str = candidate.getName().tip().toString(); }else if(o instanceof PropertyType){ final PropertyType candidate = (PropertyType) o; str = candidate.getName().tip().toString(); } if(str!=null && str.startsWith("@")){ str = str.substring(1); } return str; } @Override public String getElementQName(final Object o) { if(o instanceof Fake){ final Fake candidate = (Fake) o; return NamesExt.toExpandedString(candidate.name); }else if(o instanceof Feature){ final Feature candidate = (Feature) o; return NamesExt.toExpandedString(candidate.getType().getName()); }else if(o instanceof Property){ final Property candidate = (Property) o; return NamesExt.toExpandedString(candidate.getName()); }else if(o instanceof PropertyType){ final PropertyType candidate = (PropertyType) o; return NamesExt.toExpandedString(candidate.getName()); } return null; } @Override public String getAttributeNamespaceUri(final Object o) { return getElementNamespaceUri(o); } @Override public String getAttributeName(final Object o) { String str = null; if(o instanceof Fake){ final Fake candidate = (Fake) o; str = candidate.name.tip().toString(); }else if(o instanceof Property){ final Property candidate = (Property) o; str = candidate.getName().tip().toString(); }else if(o instanceof PropertyType){ final PropertyType candidate = (PropertyType) o; str = candidate.getName().tip().toString(); } if(str!=null && str.startsWith("@")){ str = str.substring(1); } return str; } @Override public String getAttributeQName(final Object o) { //final Identifier id = (Identifier) o; return "Id"; } @Override public boolean isDocument(Object o) { if(o instanceof Fake) o = ((Fake)o).value; return o instanceof Feature || o instanceof FeatureType ; } @Override public boolean isElement(final Object o) { return o instanceof Property || o instanceof PropertyType || o instanceof Feature || o instanceof Fake; } @Override public boolean isAttribute(final Object o) { return o instanceof Identifier; } @Override public boolean isNamespace(final Object o) { return false; } @Override public boolean isComment(final Object o) { return false; } @Override public boolean isText(final Object o) { return false; } @Override public boolean isProcessingInstruction(final Object o) { return false; } @Override public String getCommentStringValue(final Object o) { throw new UnsupportedOperationException("Not supported, should never be called, " + o); } @Override public String getElementStringValue(Object o) { if(o instanceof Fake) o = ((Fake)o).value; if(o instanceof Property){ final Property candidate = (Property) o; if(candidate instanceof Attribute && ((Attribute)candidate).getType().getMaximumOccurs()>1){ final Collection values = ((Attribute)candidate).getValues(); return (values.isEmpty())? EMPTY : values.iterator().next().toString(); }else{ final Object value = candidate.getValue(); return (value==null)? EMPTY : value.toString(); } }else if(o instanceof PropertyType){ final PropertyType candidate = (PropertyType) o; return NamesExt.toExpandedString(candidate.getName()); } return null; } @Override public String getAttributeStringValue(final Object o) { final Identifier property = (Identifier) o; return property.getID().toString(); } @Override public XPath parseXPath(final String string) throws SAXPathException { throw new UnsupportedOperationException("Not supported, should never be called"); // return new JaxenFeatureXPath(string); } //////////////////////////////////////////////////////////////////////////// // NOT NEEDED ////////////////////////////////////////////////////////////// @Override public String getNamespaceStringValue(final Object o) { throw new UnsupportedOperationException("Not supported, should never be called"); } @Override public String getTextStringValue(final Object o) { throw new UnsupportedOperationException("Not supported, should never be called"); } @Override public String getNamespacePrefix(final Object o) { throw new UnsupportedOperationException("Not supported, should never be called"); } @Override public String getProcessingInstructionTarget(final Object o) { throw new UnsupportedOperationException("Not supported, should never be called"); } @Override public String getProcessingInstructionData(final Object o) { throw new UnsupportedOperationException("Not supported, should never be called"); } //////////////////////////////////////////////////////////////////////////// // ITERATORS /////////////////////////////////////////////////////////////// @Override public Iterator getChildAxisIterator(Object o) throws UnsupportedAxisException { if(o instanceof Fake) o = ((Fake)o).value; if(o instanceof Feature){ final Feature candidate = (Feature) o; return new PropIterator(candidate,false); }else if(o instanceof FeatureAssociation){ final FeatureAssociation ct = (FeatureAssociation) o; final Collection<Feature> features = ct.getValues(); return features.iterator(); }else if(o instanceof FeatureType){ final FeatureType ct = (FeatureType) o; return new PropTypeIterator(ct.getProperties(true).iterator(),false); }else if(o instanceof FeatureAssociationRole){ final FeatureAssociationRole ct = (FeatureAssociationRole) o; try { return new PropTypeIterator(ct.getValueType().getProperties(true).iterator(),false); } catch(IllegalStateException ex) { //TODO : can happen when the feature type relations are uncomplete //may be normal or a bug } } return JaxenConstants.EMPTY_ITERATOR; } @Override public Iterator getDescendantAxisIterator(final Object o) throws UnsupportedAxisException { return new DescendantAxisIterator(o, this); } @Override public Iterator getParentAxisIterator(final Object o) throws UnsupportedAxisException { throw new UnsupportedOperationException("Not supported yet."); } @Override public Iterator getAncestorAxisIterator(final Object o) throws UnsupportedAxisException { return new AncestorAxisIterator(o, this); } @Override public Iterator getFollowingSiblingAxisIterator(final Object o) throws UnsupportedAxisException { return new FollowingSiblingAxisIterator(o, this); } @Override public Iterator getPrecedingSiblingAxisIterator(final Object o) throws UnsupportedAxisException { return new PrecedingSiblingAxisIterator(o, this); } @Override public Iterator getFollowingAxisIterator(final Object o) throws UnsupportedAxisException { return new FollowingAxisIterator(o, this); } @Override public Iterator getPrecedingAxisIterator(final Object o) throws UnsupportedAxisException { return new PrecedingAxisIterator(o, this); } @Override public Iterator getAttributeAxisIterator(Object o) throws UnsupportedAxisException { if(o instanceof Fake) o = ((Fake)o).value; if(o instanceof Feature){ final Feature att = (Feature) o; return new PropIterator(att, true); } else if(o instanceof PropertyType){ final PropertyType type = (PropertyType) o; if(type instanceof FeatureAssociationRole){ final FeatureAssociationRole ct = (FeatureAssociationRole) type; return new PropTypeIterator(ct.getValueType().getProperties(true).iterator(), true); }else{ return JaxenConstants.EMPTY_ITERATOR; } } else if(o instanceof FeatureType){ final FeatureType ct = (FeatureType) o; return new PropTypeIterator(ct.getProperties(true).iterator(), true); } return JaxenConstants.EMPTY_ITERATOR; } @Override public Iterator getNamespaceAxisIterator(final Object o) throws UnsupportedAxisException { throw new UnsupportedOperationException("Not supported yet."); } @Override public Iterator getSelfAxisIterator(final Object o) throws UnsupportedAxisException { return new SelfAxisIterator(o); } @Override public Iterator getDescendantOrSelfAxisIterator(final Object o) throws UnsupportedAxisException { return new DescendantOrSelfAxisIterator(o, this); } @Override public Iterator getAncestorOrSelfAxisIterator(final Object o) throws UnsupportedAxisException { return new AncestorOrSelfAxisIterator(o, this); } //INTERFACE //////////////////////////////////////////////////////////////// @Override public Object getDocument(final String string) throws FunctionCallException { throw new UnsupportedOperationException("Not supported yet."); } @Override public Object getDocumentNode(final Object o) { if(o instanceof Feature){ return o; }else if(o instanceof FeatureType){ return o; } return null; } @Override public Object getParentNode(final Object o) throws UnsupportedAxisException { throw new UnsupportedAxisException("Not supported. Expression on feature can only be forward."); } @Override public String translateNamespacePrefixToUri(final String string, final Object o) { throw new UnsupportedOperationException("Not supported yet."); } @Override public Object getElementById(final Object o, final String string) { throw new UnsupportedOperationException("Not supported yet."); } @Override public short getNodeType(final Object o) { throw new UnsupportedOperationException("Not supported yet."); } private static class PropTypeIterator implements Iterator{ private final Iterator ite; private final boolean attributes; private Object next = null; public PropTypeIterator(Iterator<? extends PropertyType> ite, boolean attributes) { this.ite = ite; this.attributes = attributes; } @Override public boolean hasNext() { findNext(); return next!=null; } @Override public Object next() { findNext(); if(next==null) throw new NoSuchElementException(); Object n = next; next = null; return n; } @Override public void remove() { } private void findNext(){ while(ite.hasNext() && next==null){ final Object candidate = ite.next(); GenericName name = null; if(candidate instanceof PropertyType){ name = ((PropertyType)candidate).getName(); } if(name!=null){ if(attributes){ next = name.tip().toString().startsWith("@") ? candidate : null; }else{ next = !name.tip().toString().startsWith("@") ? candidate : null; } } } } } private static class PropIterator implements Iterator{ private final Feature feature; private final Iterator ite; private Object next = null; public PropIterator(Feature f, boolean attributes) { this.feature = f; //we must unloop multi-attributes and multi-association final List props = new ArrayList<>(); final Iterator<? extends PropertyType> pite = f.getType().getProperties(true).iterator(); while(pite.hasNext() && next==null){ final PropertyType candidate = pite.next(); final GenericName gname = candidate.getName(); final String name = candidate.getName().toString(); final boolean isAtt = candidate.getName().tip().toString().startsWith("@"); if((attributes && !isAtt) || (!attributes && isAtt)){ continue; } // if(candidate instanceof AttributeType && ((AttributeType)candidate).getMaximumOccurs()>1){ // for(Object o : ((Collection)values)){ // props.add(new Fake(gname,o)); // } // }else if(candidate instanceof FeatureAssociationRole && ((FeatureAssociationRole)candidate).getMaximumOccurs()>1){ final FeatureAssociation complete = (FeatureAssociation)feature.getProperty(name); final Collection<? extends Feature> values = (Collection)complete.getValues(); for(Feature o : values){ props.add(new Fake(complete,gname,o)); } }else{ props.add(feature.getProperty(name)); } } ite = props.iterator(); } @Override public boolean hasNext() { return ite.hasNext(); } @Override public Object next() { return ite.next(); } @Override public void remove() { } } public static final class Fake{ public final GenericName name; public final Feature value; public final FeatureAssociation complete; public Fake(FeatureAssociation complete, GenericName name, Feature value) { this.complete = complete; this.name = name; this.value = value; } } }