Package org.geotools.gml3.bindings

Source Code of org.geotools.gml3.bindings.ComplexSupportXSAnyTypeBinding

/*
*    GeoTools - The Open Source Java GIS Toolkit
*    http://geotools.org
*
*    (C) 2004-2009, Open Source Geospatial Foundation (OSGeo)
*
*    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.geotools.gml3.bindings;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.xml.XMLConstants;
import javax.xml.namespace.QName;

import org.eclipse.xsd.XSDElementDeclaration;
import org.eclipse.xsd.XSDFactory;
import org.eclipse.xsd.XSDParticle;
import org.eclipse.xsd.XSDTypeDefinition;
import org.geotools.feature.NameImpl;
import org.geotools.feature.type.FeatureTypeImpl;
import org.geotools.gml3.XSDIdRegistry;
import org.geotools.util.Converters;
import org.geotools.xlink.XLINK;
import org.geotools.xml.Schemas;
import org.geotools.xs.XS;
import org.geotools.xs.bindings.XSAnyTypeBinding;
import org.opengis.feature.Attribute;
import org.opengis.feature.ComplexAttribute;
import org.opengis.feature.GeometryAttribute;
import org.opengis.feature.Property;
import org.opengis.feature.type.Name;
import org.opengis.feature.type.PropertyDescriptor;
import org.opengis.filter.identity.Identifier;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.Attributes;

/**
* A replacement for {@link XSAnyTypeBinding} that adds support for {@link ComplexAttribute} and
* related behaviours.
*
* <p>
*
* This binding that searches the substitution group of XSD element children to find properties of a
* complex attribute. This is necessary to support the GML property type pattern, in which a
* property (a property-type type) contains a property that is a member of a substitution group.
* gml:AttributeType is the canonical example of the property type pattern.
*
* <p>
*
* gml:FeaturePropertyType is an example of the property type pattern that has an explicit binding
* {@link FeaturePropertyTypeBinding}, but because an application schema may define more property
* types whose names are not known at compile time, a binding like
* {@link FeaturePropertyTypeBinding} cannot be written. This class exists to handle these
* application-schema-defined property types.
*
* <p>
*
* This class supports the encoding of XML complexType with simpleContent through extraction of a
* simpleContent property, as well as encoding XML attributes stored in the UserData map.
*
* @author Ben Caradoc-Davies, CSIRO Earth Science and Resource Engineering
*
*
* @source $URL$
*/
public class ComplexSupportXSAnyTypeBinding extends XSAnyTypeBinding {

    private XSDIdRegistry idSet;

    public ComplexSupportXSAnyTypeBinding(XSDIdRegistry idRegistry) {
        this.idSet = idRegistry;
    }

    /**
     * @see org.geotools.xml.AbstractComplexBinding#getProperty(java.lang.Object,
     *      javax.xml.namespace.QName)
     */
    @Override
    public Object getProperty(Object object, QName name) throws Exception {
        if (object instanceof ComplexAttribute) {
            ComplexAttribute complex = (ComplexAttribute) object;
            Property property = complex.getProperty(toTypeName(name));
            if (property != null && !(property instanceof ComplexAttribute)) {
                return property.getValue();
            }
            if ("id".equals(name.getLocalPart())) {
                return complex.getIdentifier();
            }
        }
        return null;
    }

    /**
     * Convert a {@link QName} to a {@link Name}.
     *
     * @param name
     * @return
     */
    private static Name toTypeName(QName name) {
        if (XMLConstants.NULL_NS_URI.equals(name.getNamespaceURI())) {
            return new NameImpl(name.getLocalPart());
        } else {
            return new NameImpl(name.getNamespaceURI(), name.getLocalPart());
        }
    }

    /**
     * @see org.geotools.xml.AbstractComplexBinding#getProperties(java.lang.Object,
     *      org.eclipse.xsd.XSDElementDeclaration)
     */  
    @SuppressWarnings("unchecked")
    @Override
    public List getProperties(Object object, XSDElementDeclaration element) throws Exception {
        if(object == null) {
            return null;
        }
       
        List<Object[/* 2 */]> properties = new ArrayList<Object[/* 2 */]>();
        XSDTypeDefinition typeDef = element.getTypeDefinition();
        boolean isAnyType = typeDef.getName() != null && typeDef.getTargetNamespace() != null
                && typeDef.getName().equals(XS.ANYTYPE.getLocalPart())
                && typeDef.getTargetNamespace().equals(XS.NAMESPACE);
        if (isAnyType) {
            Collection complexAtts;
            if (object instanceof Collection) {
                // collection of features
                complexAtts = (Collection) object;
            } else if (object instanceof ComplexAttribute) {
                // get collection of features from this attribute
                complexAtts = ((ComplexAttribute) object).getProperties();
            } else {
                return null;
            }
            for (Object complex : complexAtts) {
                if (complex instanceof ComplexAttribute) {
                    PropertyDescriptor descriptor = ((Attribute) complex).getDescriptor();
                    if (descriptor.getUserData() != null) {
                        Object propertyElement = descriptor.getUserData().get(
                                XSDElementDeclaration.class);
                        if (propertyElement != null
                                && propertyElement instanceof XSDElementDeclaration) {
                            XSDParticle substitutedChildParticle = XSDFactory.eINSTANCE
                                    .createXSDParticle();
                            substitutedChildParticle.setMaxOccurs(descriptor.getMaxOccurs());
                            substitutedChildParticle.setMinOccurs(descriptor.getMinOccurs());
                            XSDElementDeclaration wrapper = XSDFactory.eINSTANCE
                                    .createXSDElementDeclaration();
                            wrapper
                                    .setResolvedElementDeclaration((XSDElementDeclaration) propertyElement);
                            substitutedChildParticle.setContent(wrapper);
                            properties.add(new Object[] { substitutedChildParticle, complex });
                        }
                    }
                }
            }
            return properties;
        }
        if (object instanceof ComplexAttribute) {
            ComplexAttribute complex = (ComplexAttribute) object;
            for (XSDParticle childParticle : (List<XSDParticle>) Schemas.getChildElementParticles(
                    element.getTypeDefinition(), true)) {
                XSDElementDeclaration childElement = (XSDElementDeclaration) childParticle
                        .getContent();
                if (childElement.isElementDeclarationReference()) {
                    childElement = childElement.getResolvedElementDeclaration();
                }
                for (XSDElementDeclaration e : (List<XSDElementDeclaration>) childElement
                        .getSubstitutionGroup()) {
                    Name name = new NameImpl(e.getTargetNamespace(), e.getName());
                    Collection<Property> nameProperties = complex.getProperties(name);
                    if (!nameProperties.isEmpty()) {
                        // Particle creation stolen from BindingPropertyExtractor.
                        // I do not know why a wrapper is required; monkey see, monkey do.
                        // Without the wrapper, get an NPE in BindingPropertyExtractor.
                        XSDParticle substitutedChildParticle = XSDFactory.eINSTANCE
                                .createXSDParticle();
                        substitutedChildParticle.setMaxOccurs(childParticle.getMaxOccurs());
                        substitutedChildParticle.setMinOccurs(childParticle.getMinOccurs());
                        XSDElementDeclaration wrapper = XSDFactory.eINSTANCE
                                .createXSDElementDeclaration();
                        wrapper.setResolvedElementDeclaration(e);
                        substitutedChildParticle.setContent(wrapper);
                        for (Property property : nameProperties) {
                            /*
                             * Note : Returning simple feature value is not necessary as it has been
                             * taken care in the 1st For Loop of BindingPropertyExtractor.java -
                             * List properties(Object, XSDElementDeclaration) method
                             */
                            if (property instanceof ComplexAttribute) {
                                properties.add(new Object[] { substitutedChildParticle, property });
                            } else if (property instanceof GeometryAttribute) {
                                Object attType = complex.getType().getUserData()
                                        .get(XSDTypeDefinition.class);
                                boolean duplicate = false;
                                // handle substitution group for geometries too, but make sure
                                // it's not already handled in BindingPropertyExtractor
                                // otherwise it would be encoded as xlink:href as the id has already
                                // been seen
                                if (attType != null && attType instanceof XSDTypeDefinition) {
                                    XSDTypeDefinition attTypeDef = (XSDTypeDefinition) attType;
                                    for (XSDParticle attChild : (List<XSDParticle>) Schemas
                                            .getChildElementParticles(attTypeDef, true)) {
                                        XSDElementDeclaration childEl = (XSDElementDeclaration) attChild
                                                .getContent();
                                        if (childEl.isElementDeclarationReference()) {
                                            childEl = childEl.getResolvedElementDeclaration();
                                        }
                                        if (childEl.equals(e)) {
                                            duplicate = true;
                                            break;
                                        }
                                    }
                                }
                                if (!duplicate) {
                                    properties.add(new Object[] { substitutedChildParticle,
                                            property.getValue() });
                                }
                            }
                        }
                    }
                }
            }
        }

        List<XSDParticle> anyElementParticles = new ArrayList<XSDParticle>(Schemas
                .getAnyElementParticles(element.getTypeDefinition()));
        if (anyElementParticles.size() > 0) {
            Collection complexAtts = null;
            if (object instanceof Collection) {
                // collection of features
                complexAtts = (Collection) object;
            } else if (object instanceof ComplexAttribute) {
                // get collection of features from this attribute
                complexAtts = ((ComplexAttribute) object).getProperties();
            }

            // If child elements can't be retrieved from
            // Schemas.getChildElementParticles(element.getTypeDefinition(), true)),
            // properties list is empty. In this case, add all the complex
            // attributes in the object to the properties list.
            if (properties.isEmpty()) {
                for (Object complex : complexAtts) {
                    // only process complex attributes
                    if (complex instanceof ComplexAttribute) {
                        ComplexAttribute newComplexAtt = (ComplexAttribute) complex;
                        PropertyDescriptor descriptor = newComplexAtt.getDescriptor();
                        if (descriptor.getUserData() != null) {
                            Object propertyElement = descriptor.getUserData().get(
                                    XSDElementDeclaration.class);
                            if (propertyElement != null
                                    && propertyElement instanceof XSDElementDeclaration) {
                                XSDParticle substitutedChildParticle = XSDFactory.eINSTANCE
                                        .createXSDParticle();
                                substitutedChildParticle.setMaxOccurs(descriptor.getMaxOccurs());
                                substitutedChildParticle.setMinOccurs(descriptor.getMinOccurs());
                                XSDElementDeclaration wrapper = XSDFactory.eINSTANCE
                                        .createXSDElementDeclaration();
                                wrapper
                                        .setResolvedElementDeclaration((XSDElementDeclaration) propertyElement);
                                substitutedChildParticle.setContent(wrapper);
                                properties.add(new Object[] { substitutedChildParticle,
                                        newComplexAtt });
                            }
                        }
                    }
                }
            }

            /*
             * properties list is not empty.
             *
             * It's possible <any> and <element> co-exist in the same type. For example,
             *
             * <sequence>
             *     <any/>
             *     <element name="..." type="...">
             * </sequence>
             *
             * In this case, only add <any> complex attributes to the properties list. The following
             * code is not covered in unit test, as app-schema, doesn't support <any> as a
             * targetAttribute in a mapping file.
             */
            else {
                List<XSDParticle> elementParticles = new ArrayList<XSDParticle>(Schemas
                        .getChildElementParticles(element.getTypeDefinition(), false));
                for (Object complex : complexAtts) {
                    if (complex instanceof ComplexAttribute) {
                        ComplexAttribute newComplexAtt = (ComplexAttribute) complex;
                        PropertyDescriptor descriptor = ((Attribute) complex).getDescriptor();
                        if (descriptor.getUserData() != null) {
                            Object propertyElement = descriptor.getUserData().get(
                                    XSDElementDeclaration.class);
                            if (propertyElement != null
                                    && propertyElement instanceof XSDElementDeclaration) {
                                XSDParticle substitutedChildParticle = XSDFactory.eINSTANCE
                                        .createXSDParticle();
                                substitutedChildParticle.setMaxOccurs(descriptor.getMaxOccurs());
                                substitutedChildParticle.setMinOccurs(descriptor.getMinOccurs());
                                XSDElementDeclaration wrapper = XSDFactory.eINSTANCE
                                        .createXSDElementDeclaration();
                                wrapper
                                        .setResolvedElementDeclaration((XSDElementDeclaration) propertyElement);
                                substitutedChildParticle.setContent(wrapper);
                                boolean propertyExist = false;
                                for (XSDParticle childParticle : elementParticles) {
                                    XSDElementDeclaration childElement = (XSDElementDeclaration) childParticle
                                            .getContent();
                                    if (childElement.isElementDeclarationReference()) {
                                        childElement = childElement.getResolvedElementDeclaration();
                                    }
                                    String existingName = childElement.getName();
                                    String newName = newComplexAtt.getDescriptor().getName()
                                            .getLocalPart();
                                    if (existingName.equals(newName)) {
                                        propertyExist = true;
                                        break;
                                    }
                                }
                                if (!propertyExist) {
                                    properties.add(new Object[] { substitutedChildParticle,
                                            newComplexAtt });
                                }
                            }
                        }
                    }
                }
            }

        }
        return properties;
    }
   
    /**
     * Check if the complex attribute contains a feature which id is pre-existing in the document.
     * If it's true, make sure it's only encoded as an xlink:href to the existing id.
     *
     * @param value
     *            The complex attribute value
     * @param att
     *            The complex attribute itself
     */
    private void checkXlinkHref(Object value, ComplexAttribute att) {
        if (value != null && value instanceof ComplexAttribute) {
            ComplexAttribute object = (ComplexAttribute) value;
            // Only worry about features for now, as non feature types don't get ids encoded yet.
            // See GEOS-3738. To encode xlink:href to an id that doesn't exist in the doc is wrong
            if (!(object.getType() instanceof FeatureTypeImpl)) {
                // we are checking the type, not the object as FeatureImpl, because they could still
                // be non-features that are constructed as features for the purpose of feature
                // chaining.
                return;
            }
            Identifier ident = object.getIdentifier();
            if (ident == null) {
                return;
            }
            String id = Converters.convert(ident.getID(), String.class);
            if (idSet.idExists(id)) {
                // XSD type ids can only appear once in the same document, otherwise the document is
                // not schema valid. Attributes of the same ids should be encoded as xlink:href to
                // the existing attribute.
                Object clientProperties = att.getUserData().get(Attributes.class);
                Map<Name, Object> map = null;
                if (clientProperties == null) {
                    map = new HashMap<Name, Object>();
                    att.getUserData().put(Attributes.class, map);
                } else {
                    map = (Map<Name, Object>) clientProperties;
                }
                map.put(toTypeName(XLINK.HREF), "#" + id.toString());
                // make sure the value is not encoded
                att.setValue(Collections.emptyList());
            }
        }
        return;
    }

    /**
     * @see org.geotools.xml.AbstractComplexBinding#encode(java.lang.Object, org.w3c.dom.Document,
     *      org.w3c.dom.Element)
     */
    @Override
    public Element encode(Object object, Document document, Element value) throws Exception {
        if (object instanceof ComplexAttribute) {
            ComplexAttribute complex = (ComplexAttribute) object;
            if (complex.getProperties().size() == 1) {
                Property prop = complex.getProperties().iterator().next();
                checkXlinkHref(prop, complex);
            }
            GML3EncodingUtils.encodeClientProperties(complex, value);
            GML3EncodingUtils.encodeSimpleContent(complex, document, value);
        }
        return value;
    }

}
TOP

Related Classes of org.geotools.gml3.bindings.ComplexSupportXSAnyTypeBinding

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.