0001: /*
0002: * Geotools2 - OpenSource mapping toolkit
0003: * http://geotools.org
0004: * (C) 2005-2006, GeoTools Project Managment Committee (PMC)
0005: *
0006: * This library is free software; you can redistribute it and/or
0007: * modify it under the terms of the GNU Lesser General Public
0008: * License as published by the Free Software Foundation;
0009: * version 2.1 of the License.
0010: *
0011: * This library is distributed in the hope that it will be useful,
0012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0014: * Lesser General Public License for more details.
0015: *
0016: */
0017:
0018: package org.geotools.data.complex;
0019:
0020: import java.io.IOException;
0021: import java.util.AbstractList;
0022: import java.util.ArrayList;
0023: import java.util.Arrays;
0024: import java.util.Collection;
0025: import java.util.Collections;
0026: import java.util.HashMap;
0027: import java.util.HashSet;
0028: import java.util.Iterator;
0029: import java.util.LinkedList;
0030: import java.util.List;
0031: import java.util.Map;
0032: import java.util.Set;
0033: import java.util.Map.Entry;
0034: import java.util.logging.Logger;
0035:
0036: import javax.xml.namespace.QName;
0037:
0038: import org.apache.commons.collections.map.LinkedMap;
0039: import org.geotools.data.DefaultQuery;
0040: import org.geotools.data.Query;
0041: import org.geotools.data.complex.filter.XPath;
0042: import org.geotools.data.complex.filter.XPath.Step;
0043: import org.geotools.data.complex.filter.XPath.StepList;
0044: import org.geotools.feature.iso.AttributeFactoryImpl;
0045: import org.geotools.feature.iso.AttributeImpl;
0046: import org.geotools.feature.iso.ComplexAttributeImpl;
0047: import org.geotools.feature.iso.FeatureImpl;
0048: import org.geotools.feature.iso.Types;
0049: import org.geotools.feature.iso.attribute.GeometricAttribute;
0050: import org.geotools.filter.FilterAttributeExtractor;
0051: import org.geotools.filter.FilterFactoryImplNamespaceAware;
0052: import org.opengis.feature.Attribute;
0053: import org.opengis.feature.ComplexAttribute;
0054: import org.opengis.feature.Feature;
0055: import org.opengis.feature.GeometryAttribute;
0056: import org.opengis.feature.Property;
0057: import org.opengis.feature.simple.SimpleFeature;
0058: import org.opengis.feature.type.AttributeDescriptor;
0059: import org.opengis.feature.type.AttributeType;
0060: import org.opengis.feature.type.ComplexType;
0061: import org.opengis.feature.type.FeatureType;
0062: import org.opengis.feature.type.GeometryType;
0063: import org.opengis.feature.type.Name;
0064: import org.opengis.feature.type.PropertyDescriptor;
0065: import org.opengis.filter.FilterFactory;
0066: import org.opengis.filter.expression.Expression;
0067: import org.opengis.filter.expression.PropertyName;
0068: import org.opengis.referencing.crs.CoordinateReferenceSystem;
0069: import org.xml.sax.Attributes;
0070: import org.xml.sax.helpers.NamespaceSupport;
0071:
0072: /**
0073: * An alternative strategy to fetch grouped multivalued content.
0074: *
0075: * @author Gabriel Roldan
0076: * @version $Id: GroupingFeatureIterator2.java 27773 2007-11-06 23:06:54Z
0077: * groldan $
0078: * @source $URL:
0079: * http://svn.geotools.org/geotools/branches/2.4.x/modules/unsupported/community-schemas/community-schema-ds/src/main/java/org/geotools/data/complex/GroupingFeatureIterator.java $
0080: * @since 2.4
0081: */
0082: class GroupingFeatureIterator3 extends AbstractMappingFeatureIterator {
0083: private static final Logger LOGGER = org.geotools.util.logging.Logging
0084: .getLogger(GroupingFeatureIterator3.class.getPackage()
0085: .getName());
0086:
0087: /**
0088: * maxFeatures restriction value as provided by query
0089: */
0090: private int maxFeatures;
0091:
0092: /**
0093: *
0094: */
0095: private Feature curSrcFeature;
0096:
0097: /** counter to ensure maxFeatures is not exceeded */
0098: private int featureCounter;
0099:
0100: /**
0101: * List of attribute mappings marked as multivalued, and any attribute which
0102: * maps to a child attribute of any multivalued mapping. The keys in the map
0103: * are attribute mappings explicitly set as multivalued. The values in the
0104: * map are the list of mappings whose target xpath expressions correspond to
0105: * child properties of the target xpath of the mapping used as key.
0106: */
0107: private LinkedMap multivaluedMappings;
0108:
0109: /**
0110: * List of source property names referenced by multivalued mappings and its
0111: * childs.
0112: */
0113: private Set /* String */multiValuedSourcePropNames;
0114:
0115: /**
0116: * List of attribute mappings not marked as multivalued and whose target
0117: * xpath is not a child of any multivalued attribute.
0118: */
0119: private List /* AttributeMapping */singleValuedMappings;
0120:
0121: /**
0122: * flag to avoid fetching multiple source feature in repeated calld to
0123: * hasNext() without calls to next() in the middle
0124: */
0125: private boolean hasNextCalled;
0126:
0127: private FilterFactory namespaceAwareFilterFactory;
0128:
0129: private XPath xpathAttributeBuilder;
0130:
0131: public GroupingFeatureIterator3(final ComplexDataStore store,
0132: final FeatureTypeMapping mappings, final Query query)
0133: throws IOException {
0134: super (store, mappings, query);
0135: xpathAttributeBuilder = new XPath();
0136: // xpathAttributeBuilder.setFeatureFactory(super.attf);
0137: // xpathAttributeBuilder.setFeatureFactory(new MutableFeatureFactory());
0138: super .attf = new MutableFeatureFactory();
0139: xpathAttributeBuilder.setFeatureFactory(super .attf);
0140: NamespaceSupport namespaces = mappings.getNamespaces();
0141: // FilterFactory namespaceAwareFilterFactory =
0142: // CommonFactoryFinder.getFilterFactory(hints);
0143: namespaceAwareFilterFactory = new FilterFactoryImplNamespaceAware(
0144: namespaces);
0145: xpathAttributeBuilder
0146: .setFilterFactory(namespaceAwareFilterFactory);
0147:
0148: splitMappings();
0149: }
0150:
0151: protected Query getUnrolledQuery(Query query) {
0152: maxFeatures = query.getMaxFeatures();
0153: Query unmappedQuery = store.unrollQuery(query, mapping);
0154: ((DefaultQuery) unmappedQuery)
0155: .setMaxFeatures(Integer.MAX_VALUE);
0156:
0157: unmappedQuery = ensureGroupingAttsPresent(unmappedQuery);
0158:
0159: return unmappedQuery;
0160: }
0161:
0162: public boolean hasNext() {
0163: if (hasNextCalled) {
0164: return curSrcFeature != null;
0165: }
0166:
0167: boolean exists = false;
0168:
0169: if (sourceFeatures != null && featureCounter < maxFeatures) {
0170:
0171: exists = this .sourceFeatures.hasNext();
0172:
0173: if (exists && this .curSrcFeature == null) {
0174: this .curSrcFeature = (Feature) this .sourceFeatures
0175: .next();
0176: }
0177: }
0178:
0179: if (!exists) {
0180: LOGGER.finest("no more features, produced "
0181: + featureCounter);
0182: }
0183:
0184: hasNextCalled = true;
0185: if (!exists) {
0186: close();
0187: }
0188: return exists;
0189: }
0190:
0191: public Object next() {
0192: if (!hasNext()) {
0193: throw new IllegalStateException(
0194: "there are no more features in this iterator");
0195: }
0196: Feature next;
0197: try {
0198: next = computeNext();
0199: } catch (IOException e) {
0200: close();
0201: throw new RuntimeException(e);
0202: }
0203: hasNextCalled = false;
0204: ++featureCounter;
0205: return next;
0206: }
0207:
0208: // ///////////////////
0209:
0210: /**
0211: *
0212: */
0213: private Feature computeNext() throws IOException {
0214: assert this .curSrcFeature != null : "hastNext not called?";
0215:
0216: // get the mapping set of a feature attribute
0217: final AttributeDescriptor targetNode = mapping
0218: .getTargetFeature();
0219:
0220: // create the target feature and iterate in the source ones to set its
0221: // values.
0222: final String fid = extractIdForFeature(curSrcFeature);
0223: final Feature targetFeature = attf.createFeature(null,
0224: targetNode, fid);
0225:
0226: setNonMultivaluedAttributes(targetFeature);
0227:
0228: setMultiValuedAttributes(targetFeature);
0229:
0230: if (!sourceFeatures.hasNext()) {
0231: close();
0232: }
0233: return targetFeature;
0234: }
0235:
0236: private void setNonMultivaluedAttributes(final Feature targetFeature)
0237: throws IOException {
0238: AttributeMapping mapping;
0239: for (Iterator it = this .singleValuedMappings.iterator(); it
0240: .hasNext();) {
0241: mapping = (AttributeMapping) it.next();
0242: StepList targetXPath = mapping.getTargetXPath();
0243: if (targetXPath.size() == 1) {
0244: Step rootStep = (Step) targetXPath.get(0);
0245: QName stepName = rootStep.getName();
0246: if (Types.equals(targetFeature.getDescriptor()
0247: .getName(), stepName)) {
0248: // ignore the top level mapping for the Feature
0249: // itself
0250: // as it was already set
0251: continue;
0252: }
0253: }
0254: setSingleValuedAttribute(targetFeature, curSrcFeature,
0255: mapping);
0256: }
0257: }
0258:
0259: private void setMultiValuedAttributes(final Feature targetFeature) {
0260: // Map sourcePropName/List<values> with the contents
0261: // of the source attributes needed to map multivalued
0262: // properties and its children
0263: final Map /* String/List<Object> */mvaluedSourceProps = new HashMap();
0264: final NamespaceSupport namespaces = mapping.getNamespaces();
0265: for (Iterator it = multiValuedSourcePropNames.iterator(); it
0266: .hasNext();) {
0267: // this is just an ugly way of getting rid of index in expressions
0268: // like "attName[2]"
0269: String attName = (String) it.next();
0270: StepList steps = XPath.steps(targetFeature.getDescriptor(),
0271: attName, namespaces);
0272: String string = ((XPath.Step) steps.get(0)).getName()
0273: .getLocalPart();
0274: mvaluedSourceProps.put(string, new ArrayList());
0275: }
0276:
0277: // // build the group of properties comprising the mvalued properties
0278: // and its children ///
0279: final List /* Name */groupByAttNames = toTypeNames(mapping
0280: .getGroupByAttNames());
0281: // the grouping values to check for equality
0282: final List baseGroupingAttributes = extractGroupingAttributes(
0283: curSrcFeature, groupByAttNames);
0284:
0285: final Feature firstFeatureInGroup = curSrcFeature;
0286: extractSourceProperties(mvaluedSourceProps,
0287: (SimpleFeature) curSrcFeature);
0288:
0289: List currFeatureGroupingAtts;
0290: int index = 0;
0291: while (sourceFeatures.hasNext()) {
0292: curSrcFeature = (Feature) sourceFeatures.next();
0293: currFeatureGroupingAtts = extractGroupingAttributes(
0294: curSrcFeature, groupByAttNames);
0295: if (baseGroupingAttributes.equals(currFeatureGroupingAtts)) {
0296: extractSourceProperties(mvaluedSourceProps,
0297: (SimpleFeature) curSrcFeature);
0298: index++;
0299: } else {
0300: break;
0301: }
0302: }
0303:
0304: // /now set the mapping properties explicitly marked as multivalued as a
0305: // lazy
0306: // list of properties for its enclosing property in the target feature
0307: for (Iterator it = multivaluedMappings.entrySet().iterator(); it
0308: .hasNext();) {
0309: final Map.Entry entry = (Entry) it.next();
0310: final AttributeMapping mvaluedProp = (AttributeMapping) entry
0311: .getKey();
0312: final List childPropMappings = (List) entry.getValue();
0313:
0314: final LazyProperties mvaluedInstances;
0315: mvaluedInstances = new LazyProperties(targetFeature,
0316: mvaluedProp, childPropMappings, mvaluedSourceProps);
0317: final String id = extractIdForAttribute(mvaluedProp
0318: .getIdentifierExpression(), firstFeatureInGroup);
0319: final AttributeType targetNodeType = mvaluedProp
0320: .getTargetNodeInstance();
0321: final StepList targetXPath = mvaluedProp.getTargetXPath();
0322:
0323: MutableAttribute parent = getParent(targetFeature,
0324: targetXPath);
0325: Collection newParentContent = mvaluedInstances;
0326: // may the parent have pre-existing non multivalued properties?
0327: Collection previousProperties = (Collection) parent
0328: .getValue();
0329: if (previousProperties.size() > 0) {
0330: DeferredList deferredList = new DeferredList();
0331: deferredList.addAll(new ArrayList(previousProperties));
0332: deferredList.addAll(mvaluedInstances);
0333: }
0334: parent.setValue(newParentContent);
0335: }
0336: }
0337:
0338: private static class DeferredList extends AbstractList {
0339: private List deferredContent = new ArrayList(2);
0340:
0341: public boolean addAll(List c) {
0342: deferredContent.add(c);
0343: return true;
0344: }
0345:
0346: public Object get(int index) {
0347: if (index < 0 || index >= size()) {
0348: throw new IndexOutOfBoundsException(index + ": size="
0349: + size());
0350: }
0351: List deferred;
0352: int cumulativeIndex = 0;
0353: Object value = null;
0354: for (Iterator it = deferredContent.iterator(); it.hasNext();) {
0355: deferred = (List) it.next();
0356: int size = cumulativeIndex + deferred.size();
0357: if (index < size) {
0358: value = deferred.get(index - cumulativeIndex);
0359: break;
0360: }
0361: cumulativeIndex += deferred.size();
0362: }
0363: return value;
0364: }
0365:
0366: public int size() {
0367: int size = 0;
0368: for (Iterator it = deferredContent.iterator(); it.hasNext();) {
0369: List deferred = (List) it.next();
0370: size += deferred.size();
0371: }
0372: return size;
0373: }
0374: }
0375:
0376: private MutableAttribute getParent(final Feature targetFeature,
0377: final StepList childXPath) {
0378: StepList parentPath = new StepList(childXPath);
0379: parentPath.remove(childXPath.size() - 1);
0380: MutableAttribute parent = null;
0381:
0382: if (parentPath.size() > 0) {
0383: PropertyName property = namespaceAwareFilterFactory
0384: .property(parentPath.toString());
0385: parent = (MutableAttribute) property
0386: .evaluate(targetFeature);
0387: if (parent == null) {
0388: parent = (MutableAttribute) xpathAttributeBuilder.set(
0389: targetFeature, parentPath, null, null, null);
0390: }
0391: } else /* set direct child descendant of feature */
0392: {
0393: parent = (MutableAttribute) targetFeature;
0394: }
0395:
0396: return parent;
0397: }
0398:
0399: private static class MutableFeatureFactory extends
0400: AttributeFactoryImpl {
0401: public ComplexAttribute createComplexAttribute(
0402: Collection value, AttributeDescriptor desc, String id) {
0403: return new MutableComplexAttribute(value, desc, id);
0404: }
0405:
0406: public ComplexAttribute createComplexAttribute(
0407: Collection value, ComplexType type, String id) {
0408: return new MutableComplexAttribute(value, type, id);
0409: }
0410:
0411: public Feature createFeature(Collection value,
0412: AttributeDescriptor desc, String id) {
0413: return new MutableFeature(value, desc, id);
0414: }
0415:
0416: public Feature createFeature(Collection value,
0417: FeatureType type, String id) {
0418: return new MutableFeature(value, type, id);
0419: }
0420:
0421: public Attribute createAttribute(Object value,
0422: AttributeDescriptor descriptor, String id) {
0423: return new MutableAttributeImpl(value, descriptor, id);
0424: }
0425:
0426: public GeometryAttribute createGeometryAttribute(Object value,
0427: AttributeDescriptor desc, String id,
0428: CoordinateReferenceSystem crs) {
0429: return new MutableGeometryAttribute(value, desc, id, crs);
0430: }
0431: }
0432:
0433: private interface MutableAttribute extends Attribute {
0434: /**
0435: * Overrides the semantics of setValue with the following: if
0436: * <code>newValue instanceof LazyProperties</code> or
0437: * <code>newValue instanceof DeferredList</code>, the content of this
0438: * attribute is <code>newValue</code> itself, no safe copy is made.
0439: * Otherwise it behaves as usual.
0440: *
0441: */
0442: public void setValue(Object newValue);
0443:
0444: public void setIdExpression(Expression identifierExpression);
0445:
0446: public void setValueExpression(Expression sourceExpression);
0447:
0448: public void setSourceProperties(Map mvaluedSourceProps);
0449:
0450: public void setIndex(int index);
0451:
0452: public void setClientProperties(Map clientProperties);
0453: }
0454:
0455: private static class MutableAdapter {
0456:
0457: public List get(final Name name, final List properties) {
0458: LazyProperties lazyProperties = findLazyProperties(name,
0459: properties);
0460: if (lazyProperties != null) {
0461: return lazyProperties;
0462: }
0463: // JD: this is a farily lenient check, should we be stricter about
0464: // matching up the namespace
0465: List/* <Property> */childs = new LinkedList/* <Property> */();
0466:
0467: for (Iterator itr = properties.iterator(); itr.hasNext();) {
0468: Property prop = (Property) itr.next();
0469: PropertyDescriptor node = prop.descriptor();
0470: Name propName = node.getName();
0471: if (name.getNamespaceURI() != null) {
0472: if (propName.equals(name)) {
0473: childs.add(prop);
0474: }
0475: } else {
0476: // just do a local part compare
0477: String localName = propName.getLocalPart();
0478: if (localName.equals(name.getLocalPart())) {
0479: childs.add(prop);
0480: }
0481: }
0482: }
0483: return childs;
0484: }
0485:
0486: private LazyProperties findLazyProperties(Name name,
0487: List properties) {
0488: LazyProperties propsForName = null;
0489: if (properties instanceof LazyProperties) {
0490: LazyProperties lazyProperties = (LazyProperties) properties;
0491: if (lazyPropertiesElementTargetsName(name,
0492: lazyProperties)) {
0493: propsForName = lazyProperties;
0494: }
0495: } else if (properties instanceof DeferredList) {
0496: DeferredList dl = (DeferredList) properties;
0497: for (Iterator it = dl.deferredContent.iterator(); it
0498: .hasNext();) {
0499: List list = (List) it.next();
0500: if (list instanceof LazyProperties) {
0501: if (lazyPropertiesElementTargetsName(name,
0502: (LazyProperties) list)) {
0503: propsForName = (LazyProperties) list;
0504: break;
0505: }
0506: }
0507: }
0508: }
0509: return propsForName;
0510: }
0511:
0512: private boolean lazyPropertiesElementTargetsName(Name name,
0513: LazyProperties lazyProperties) {
0514: StepList targetXPath = lazyProperties.multivaluedMapping
0515: .getTargetXPath();
0516: Step lastStep = (Step) targetXPath
0517: .get(targetXPath.size() - 1);
0518: QName qname = lastStep.getName();
0519: String nameURI = name.getNamespaceURI();
0520: String nameLocal = name.getLocalPart();
0521: String qnameURI = qname.getNamespaceURI();
0522: String qnameLocal = qname.getLocalPart();
0523: if (nameURI == null && nameLocal.equals(qnameLocal)) {
0524: return true;
0525: } else if (nameURI.equals(qnameURI)
0526: && nameLocal.equals(qnameLocal)) {
0527: return true;
0528: }
0529: return false;
0530: }
0531:
0532: public Object getValue(
0533: final MutableAttribute mutableAttributeImpl,
0534: final Expression valueExpression,
0535: final Map mvaluedSourceProps, int index) {
0536: final Map indexValues = getIndexProperties(
0537: mvaluedSourceProps, index);
0538: // now let the MapPropertyAccessorFactory to evaluate the attribute
0539: // expressions
0540: Object evaluatedValue = valueExpression
0541: .evaluate(indexValues);
0542: return evaluatedValue;
0543: }
0544:
0545: private Map getIndexProperties(Map mvaluedSourceProps, int index) {
0546: Map indexValues = new HashMap();
0547: Map.Entry entry;
0548: String attName;
0549: List values;
0550: Object valueAtIndex;
0551: for (Iterator it = mvaluedSourceProps.entrySet().iterator(); it
0552: .hasNext();) {
0553: entry = (Entry) it.next();
0554: attName = (String) entry.getKey();
0555: values = (List) entry.getValue();
0556: valueAtIndex = values.get(index);
0557: indexValues.put(attName, valueAtIndex);
0558: }
0559: return indexValues;
0560: }
0561:
0562: public String getId(MutableAttribute mutableAttributeImpl,
0563: Expression identifierExpression,
0564: Map mvaluedSourceProps, int index) {
0565: String id = (String) identifierExpression.evaluate(
0566: mvaluedSourceProps, String.class);
0567: return id;
0568: }
0569:
0570: public void setChildrenIndex(final List properties,
0571: final int index) {
0572: MutableAttribute child;
0573: for (Iterator it = properties.iterator(); it.hasNext();) {
0574: child = (MutableAttribute) it.next();
0575: child.setIndex(index);
0576: }
0577: }
0578:
0579: public AttributeDescriptor getDescriptor(
0580: final AttributeDescriptor descriptor,
0581: final Map clientProperties,
0582: final Map mvaluedSourceProps, int index) {
0583: if (clientProperties != null && clientProperties.size() > 0
0584: && mvaluedSourceProps != null) {
0585: final Map indexValues = getIndexProperties(
0586: mvaluedSourceProps, index);
0587: final Map nodeAttributes = new HashMap();
0588:
0589: for (Iterator it = clientProperties.entrySet()
0590: .iterator(); it.hasNext();) {
0591: Map.Entry entry = (Map.Entry) it.next();
0592: org.opengis.feature.type.Name propName = (org.opengis.feature.type.Name) entry
0593: .getKey();
0594: Expression propExpr = (Expression) entry.getValue();
0595:
0596: Object propValue = propExpr.evaluate(indexValues);
0597:
0598: nodeAttributes.put(propName, propValue);
0599: }
0600: descriptor
0601: .putUserData(Attributes.class, nodeAttributes);
0602: }
0603:
0604: return descriptor;
0605: }
0606: }
0607:
0608: private static class MutableFeature extends FeatureImpl implements
0609: MutableAttribute {
0610: private static MutableAdapter mutableAtapter = new MutableAdapter();
0611:
0612: private Expression identifierExpression = Expression.NIL;
0613:
0614: private Map mvaluedSourceProps;
0615:
0616: private int index;
0617:
0618: private Map clientProperties;
0619:
0620: public MutableFeature(Collection values,
0621: AttributeDescriptor desc, String id) {
0622: super (values, desc, id);
0623: }
0624:
0625: public MutableFeature(Collection values, FeatureType type,
0626: String id) {
0627: super (values, type, id);
0628: }
0629:
0630: public AttributeDescriptor getDescriptor() {
0631: return mutableAtapter.getDescriptor(super .getDescriptor(),
0632: clientProperties, mvaluedSourceProps, index);
0633: }
0634:
0635: public void setValue(Object newValue) {
0636: if (newValue instanceof LazyProperties
0637: || newValue instanceof DeferredList) {
0638: super .setValue(Collections.EMPTY_LIST);
0639: properties = (List) newValue;
0640: } else {
0641: super .setValue(newValue);
0642: }
0643: }
0644:
0645: public List/* <Property> */get(Name name) {
0646: return mutableAtapter.get(name, properties);
0647: }
0648:
0649: public String getID() {
0650: if (mvaluedSourceProps != null) {
0651: return mutableAtapter.getId(this , identifierExpression,
0652: mvaluedSourceProps, index);
0653: }
0654: return super .getID();
0655: }
0656:
0657: public void setIdExpression(Expression identifierExpression) {
0658: this .identifierExpression = identifierExpression;
0659: }
0660:
0661: public void setSourceProperties(Map mvaluedSourceProps) {
0662: this .mvaluedSourceProps = mvaluedSourceProps;
0663: }
0664:
0665: public void setValueExpression(Expression sourceExpression) {
0666: if (sourceExpression != null
0667: && Expression.NIL.equals(sourceExpression)) {
0668: throw new UnsupportedOperationException(
0669: "not applicable to complex. Source expression: "
0670: + sourceExpression);
0671: }
0672: }
0673:
0674: public void setIndex(int index) {
0675: this .index = index;
0676: mutableAtapter.setChildrenIndex(properties, index);
0677: }
0678:
0679: public void setClientProperties(final Map clientProperties) {
0680: this .clientProperties = clientProperties;
0681: }
0682: }
0683:
0684: private static class MutableAttributeImpl extends AttributeImpl
0685: implements MutableAttribute {
0686: private static MutableAdapter mutableAtapter = new MutableAdapter();
0687:
0688: private Expression identifierExpression = Expression.NIL;
0689:
0690: private Map mvaluedSourceProps;
0691:
0692: private Expression valueExpression;
0693:
0694: private int index;
0695:
0696: private Map clientProperties;
0697:
0698: public MutableAttributeImpl(Object content,
0699: AttributeDescriptor descriptor, String id) {
0700: super (content, descriptor, id);
0701: }
0702:
0703: public void setIndex(int index) {
0704: this .index = index;
0705: }
0706:
0707: public AttributeDescriptor getDescriptor() {
0708: return mutableAtapter.getDescriptor(super .getDescriptor(),
0709: clientProperties, mvaluedSourceProps, index);
0710: }
0711:
0712: public Object getValue() {
0713: if (mvaluedSourceProps != null) {
0714: return mutableAtapter.getValue(this , valueExpression,
0715: mvaluedSourceProps, index);
0716: }
0717: return super .getValue();
0718: }
0719:
0720: public String getID() {
0721: if (mvaluedSourceProps != null) {
0722: return mutableAtapter.getId(this , identifierExpression,
0723: mvaluedSourceProps, index);
0724: }
0725: return super .getID();
0726: }
0727:
0728: public void setIdExpression(Expression identifierExpression) {
0729: this .identifierExpression = identifierExpression;
0730: }
0731:
0732: public void setSourceProperties(Map mvaluedSourceProps) {
0733: this .mvaluedSourceProps = mvaluedSourceProps;
0734: }
0735:
0736: public void setValueExpression(Expression sourceExpression) {
0737: this .valueExpression = sourceExpression;
0738: }
0739:
0740: public void setClientProperties(final Map clientProperties) {
0741: this .clientProperties = clientProperties;
0742: }
0743: }
0744:
0745: private static class MutableGeometryAttribute extends
0746: GeometricAttribute implements MutableAttribute {
0747: private static MutableAdapter mutableAtapter = new MutableAdapter();
0748:
0749: private Expression identifierExpression = Expression.NIL;
0750:
0751: private Map mvaluedSourceProps;
0752:
0753: private Expression valueExpression;
0754:
0755: private int index;
0756:
0757: private Map clientProperties;
0758:
0759: public MutableGeometryAttribute(Object content,
0760: AttributeDescriptor descriptor, String id,
0761: CoordinateReferenceSystem cs) {
0762: super (content, descriptor, id, cs);
0763: }
0764:
0765: public AttributeDescriptor getDescriptor() {
0766: return mutableAtapter.getDescriptor(super .getDescriptor(),
0767: clientProperties, mvaluedSourceProps, index);
0768: }
0769:
0770: public void setIndex(int index) {
0771: this .index = index;
0772: }
0773:
0774: public Object getValue() {
0775: if (mvaluedSourceProps != null) {
0776: return mutableAtapter.getValue(this , valueExpression,
0777: mvaluedSourceProps, index);
0778: }
0779: return super .getValue();
0780: }
0781:
0782: public String getID() {
0783: if (mvaluedSourceProps != null) {
0784: return mutableAtapter.getId(this , identifierExpression,
0785: mvaluedSourceProps, index);
0786: }
0787: return super .getID();
0788: }
0789:
0790: public void setIdExpression(Expression identifierExpression) {
0791: this .identifierExpression = identifierExpression;
0792: }
0793:
0794: public void setSourceProperties(Map mvaluedSourceProps) {
0795: this .mvaluedSourceProps = mvaluedSourceProps;
0796: }
0797:
0798: public void setValueExpression(Expression sourceExpression) {
0799: this .valueExpression = sourceExpression;
0800: }
0801:
0802: public void setClientProperties(final Map clientProperties) {
0803: this .clientProperties = clientProperties;
0804: }
0805: }
0806:
0807: private static class MutableComplexAttribute extends
0808: ComplexAttributeImpl implements MutableAttribute {
0809: private static MutableAdapter mutableAtapter = new MutableAdapter();
0810:
0811: private Expression identifierExpression = Expression.NIL;
0812:
0813: private Map mvaluedSourceProps;
0814:
0815: private int index;
0816:
0817: private Map clientProperties;
0818:
0819: public MutableComplexAttribute(Collection values,
0820: ComplexType type, String id) {
0821: super (values, type, id);
0822: }
0823:
0824: public MutableComplexAttribute(Collection values,
0825: AttributeDescriptor desc, String id) {
0826: super (values, desc, id);
0827: }
0828:
0829: public AttributeDescriptor getDescriptor() {
0830: return mutableAtapter.getDescriptor(super .getDescriptor(),
0831: clientProperties, mvaluedSourceProps, index);
0832: }
0833:
0834: public void setIndex(int index) {
0835: this .index = index;
0836: mutableAtapter.setChildrenIndex(properties, index);
0837: }
0838:
0839: public void setValue(Object newValue) {
0840: if (newValue instanceof LazyProperties
0841: || newValue instanceof DeferredList) {
0842: super .setValue(Collections.EMPTY_LIST);
0843: properties = (List) newValue;
0844: } else {
0845: super .setValue(newValue);
0846: }
0847: }
0848:
0849: public List/* <Property> */get(Name name) {
0850: return mutableAtapter.get(name, properties);
0851: }
0852:
0853: public String getID() {
0854: if (mvaluedSourceProps != null) {
0855: return mutableAtapter.getId(this , identifierExpression,
0856: mvaluedSourceProps, index);
0857: }
0858: return super .getID();
0859: }
0860:
0861: public void setIdExpression(Expression identifierExpression) {
0862: this .identifierExpression = identifierExpression;
0863: }
0864:
0865: public void setSourceProperties(Map mvaluedSourceProps) {
0866: this .mvaluedSourceProps = mvaluedSourceProps;
0867: }
0868:
0869: public void setValueExpression(Expression sourceExpression) {
0870: if (sourceExpression != null
0871: && !Expression.NIL.equals(sourceExpression)) {
0872: throw new UnsupportedOperationException(
0873: "not applicable to complex. Source expression: "
0874: + sourceExpression);
0875: }
0876: }
0877:
0878: public void setClientProperties(final Map clientProperties) {
0879: this .clientProperties = clientProperties;
0880: }
0881: }
0882:
0883: /**
0884: *
0885: * @param mvaluedSourceProps
0886: * @param srcFeature
0887: */
0888: private void extractSourceProperties(Map mvaluedSourceProps,
0889: final SimpleFeature srcFeature) {
0890: Map.Entry entry;
0891: String propName;
0892: List sourceValues;
0893: Object value;
0894: for (Iterator it = mvaluedSourceProps.entrySet().iterator(); it
0895: .hasNext();) {
0896: entry = (Entry) it.next();
0897: propName = (String) entry.getKey();
0898: sourceValues = (List) entry.getValue();
0899: value = srcFeature.getValue(propName);
0900: sourceValues.add(value);
0901: }
0902: }
0903:
0904: private class LazyProperties extends AbstractList {
0905: /** The multivalued attribute this list holds */
0906: private AttributeMapping multivaluedMapping;
0907:
0908: /** the mapping of the child properties of the multivalued attribute */
0909: private List/* AttributeMapping */childMappings;
0910:
0911: /**
0912: * Map of source properties values comprising the whole current group
0913: * for the target feature instance multivalued properties. The keys are
0914: * Strings representing the source property names and the values Lists
0915: * of values for the source properties in the current group.
0916: */
0917: private Map mvaluedSourceProps;
0918:
0919: /** cached group size */
0920: private int size;
0921:
0922: private LazyFeatureFactory ffac;
0923:
0924: private Feature targetFeature;
0925:
0926: public LazyProperties(Feature targetFeature,
0927: final AttributeMapping multivaluedMapping,
0928: final List childMappings, Map mvaluedSourceProps) {
0929: this .targetFeature = targetFeature;
0930: this .multivaluedMapping = multivaluedMapping;
0931: this .childMappings = childMappings;
0932: this .mvaluedSourceProps = mvaluedSourceProps;
0933: this .size = ((List) mvaluedSourceProps.values().iterator()
0934: .next()).size();
0935: this .ffac = new LazyFeatureFactory();
0936: }
0937:
0938: /**
0939: * returns the attribute targeted by the list multivalued property at
0940: * index <code>index/code>
0941: */
0942: public Object get(int index) {
0943: MutableAttribute attribute = ffac.create(
0944: multivaluedMapping, childMappings, index);
0945: attribute.setIndex(index);
0946: return attribute;
0947: }
0948:
0949: public int size() {
0950: return size;
0951: }
0952:
0953: private class LazyFeatureFactory extends AttributeFactoryImpl {
0954: Map properties = new HashMap();
0955:
0956: public MutableAttribute create(
0957: final AttributeMapping mapping,
0958: final List childMappings, final int index) {
0959: StepList targetXPath = mapping.getTargetXPath();
0960: if (!properties.containsKey(targetXPath)) {
0961: buildLazyMultivaluedAttribute(mapping,
0962: childMappings);
0963: }
0964:
0965: MutableAttribute attribute = (MutableAttribute) properties
0966: .get(targetXPath);
0967: attribute.setClientProperties(mapping
0968: .getClientProperties());
0969: // //
0970: attribute.setSourceProperties(mvaluedSourceProps);
0971:
0972: return attribute;
0973: }
0974:
0975: private void buildLazyMultivaluedAttribute(
0976: final AttributeMapping mapping,
0977: final List childMappings) {
0978: StepList targetXPath = mapping.getTargetXPath();
0979: AttributeType targetNodeType = mapping
0980: .getTargetNodeInstance();
0981: MutableAttribute parent = getParent(targetFeature,
0982: targetXPath);
0983: Step lastStep = (Step) targetXPath.get(targetXPath
0984: .size() - 1);
0985: Name name = Types.toTypeName(lastStep.getName());
0986: AttributeDescriptor descriptor;
0987: ComplexType parentType = (ComplexType) parent.getType();
0988: if (targetNodeType == null) {
0989: descriptor = (AttributeDescriptor) Types
0990: .descriptor(parentType, name);
0991: } else {
0992: descriptor = (AttributeDescriptor) Types
0993: .descriptor(parentType, name,
0994: targetNodeType);
0995: }
0996: MutableAttribute attribute = create(descriptor);
0997: attribute.setIdExpression(mapping
0998: .getIdentifierExpression());
0999:
1000: properties.put(targetXPath, attribute);
1001:
1002: for (Iterator it = childMappings.iterator(); it
1003: .hasNext();) {
1004: AttributeMapping childMapping = (AttributeMapping) it
1005: .next();
1006: StepList childXpath = new XPath.StepList(
1007: childMapping.getTargetXPath());
1008: int parentPathStepCount = targetXPath.size();
1009: for (int i = 0; i < parentPathStepCount; i++) {
1010: childXpath.remove(0);
1011: }
1012:
1013: AttributeType childNodeInstanceType = childMapping
1014: .getTargetNodeInstance();
1015: MutableAttribute child;
1016: child = (MutableAttribute) xpathAttributeBuilder
1017: .set(attribute, childXpath, null, null,
1018: childNodeInstanceType);
1019: child.setIdExpression(childMapping
1020: .getIdentifierExpression());
1021: child.setValueExpression(childMapping
1022: .getSourceExpression());
1023: child.setSourceProperties(mvaluedSourceProps);
1024: child.setClientProperties(childMapping
1025: .getClientProperties());
1026: }
1027: }
1028:
1029: private MutableAttribute create(
1030: final AttributeDescriptor descriptor) {
1031: AttributeType type = (AttributeType) descriptor.type();
1032: MutableAttribute att;
1033: if (type instanceof FeatureType) {
1034: att = new MutableFeature(null, descriptor, "fake");
1035: } else if (type instanceof ComplexType) {
1036: att = new MutableComplexAttribute(null, descriptor,
1037: null);
1038: } else if (type instanceof GeometryType) {
1039: CoordinateReferenceSystem crs = ((GeometryType) type)
1040: .getCRS();
1041: att = new MutableGeometryAttribute(null,
1042: descriptor, null, crs);
1043: } else {
1044: att = new MutableAttributeImpl(null, descriptor,
1045: null);
1046: }
1047: return att;
1048: }
1049: }
1050:
1051: private class LazyAttribute extends AttributeImpl {
1052: public LazyAttribute(Object content,
1053: AttributeDescriptor descriptor, String id) {
1054: super (content, descriptor, id);
1055: }
1056: }
1057: }
1058:
1059: private List toTypeNames(final List groupByAttNames) {
1060: List typeNames = new ArrayList(groupByAttNames.size());
1061: String sourceAttName;
1062: Name attributeName;
1063: for (Iterator it = groupByAttNames.iterator(); it.hasNext();) {
1064: sourceAttName = (String) it.next();
1065: attributeName = Types.typeName(sourceAttName);
1066: typeNames.add(attributeName);
1067: }
1068: return typeNames;
1069: }
1070:
1071: /**
1072: * Extract the attributes from grouping attributes.
1073: *
1074: * @param groupByAttNames
1075: *
1076: * @param Feature
1077: * a source feature
1078: * @return List<List<Attribute>> the the contened list has the attributes
1079: * required
1080: */
1081: private final List/* <List<Attribute>> */extractGroupingAttributes(
1082: final ComplexAttribute srcFeature,
1083: final List /* Name */groupByAttNames) {
1084:
1085: List/* <List<Attribute>> */attrGroup = new ArrayList/* <List<Attribute>> */(
1086: groupByAttNames.size());
1087:
1088: for (Iterator it = groupByAttNames.iterator(); it.hasNext();) {
1089: Name name = (Name) it.next();
1090: List/* <Attribute> */listAttrForName = srcFeature
1091: .get(name);
1092: attrGroup.add(listAttrForName);
1093: }
1094:
1095: return attrGroup;
1096: }
1097:
1098: /**
1099: * Sets the values of grouping attributes.
1100: *
1101: * @param sourceFeature
1102: * @param groupingMappings
1103: * @param targetFeature
1104: *
1105: * @return Feature. Target feature sets with simple attributes
1106: */
1107: private void setSingleValuedAttribute(final Feature target,
1108: final Feature source, final AttributeMapping attMapping)
1109: throws IOException {
1110:
1111: final Expression sourceExpression = attMapping
1112: .getSourceExpression();
1113: final AttributeType targetNodeType = attMapping
1114: .getTargetNodeInstance();
1115: final StepList xpath = attMapping.getTargetXPath();
1116:
1117: Object value = getValue(sourceExpression, source);
1118:
1119: String id = null;
1120: if (Expression.NIL != attMapping.getIdentifierExpression()) {
1121: id = extractIdForAttribute(attMapping
1122: .getIdentifierExpression(), this .curSrcFeature);
1123: }
1124: Attribute instance = xpathAttributeBuilder.set(target, xpath,
1125: value, id, targetNodeType);
1126: Map clientPropsMappings = attMapping.getClientProperties();
1127: setClientProperties(instance, source, clientPropsMappings);
1128: }
1129:
1130: private void setClientProperties(final Attribute target,
1131: final Object source, final Map clientProperties) {
1132: if (clientProperties.size() == 0) {
1133: return;
1134: }
1135: final Map nodeAttributes = new HashMap();
1136: final AttributeDescriptor node = target.getDescriptor();
1137:
1138: for (Iterator it = clientProperties.entrySet().iterator(); it
1139: .hasNext();) {
1140: Map.Entry entry = (Map.Entry) it.next();
1141: org.opengis.feature.type.Name propName = (org.opengis.feature.type.Name) entry
1142: .getKey();
1143: Expression propExpr = (Expression) entry.getValue();
1144:
1145: Object propValue = getValue(propExpr, source);
1146:
1147: nodeAttributes.put(propName, propValue);
1148: }
1149:
1150: node.putUserData(Attributes.class, nodeAttributes);
1151: }
1152:
1153: /**
1154: * Split the attribute mappings in two sets, the ones that belong to a
1155: * multivalued property {@link #multivaluedMappings} and the ones that not
1156: * {@link #singleValuedMappings}.
1157: */
1158: private void splitMappings() {
1159: this .multivaluedMappings = new LinkedMap();
1160: this .singleValuedMappings = new ArrayList(mapping
1161: .getAttributeMappings());
1162: this .multiValuedSourcePropNames = new HashSet();
1163:
1164: for (Iterator it = mapping.getAttributeMappings().iterator(); it
1165: .hasNext();) {
1166: AttributeMapping am = (AttributeMapping) it.next();
1167: if (am.isMultiValued()) {
1168: singleValuedMappings.remove(am);
1169: multivaluedMappings.put(am, new ArrayList(2));
1170: multiValuedSourcePropNames
1171: .addAll(getSourcePropertyNames(am));
1172: }
1173: }
1174:
1175: for (Iterator mvaluedPaths = multivaluedMappings.entrySet()
1176: .iterator(); mvaluedPaths.hasNext();) {
1177: final Map.Entry entry = (Entry) mvaluedPaths.next();
1178: final AttributeMapping parentMapping = (AttributeMapping) entry
1179: .getKey();
1180: final List childMappings = (List) entry.getValue();
1181: final StepList mvaluedPath = parentMapping.getTargetXPath();
1182:
1183: for (Iterator mappings = mapping.getAttributeMappings()
1184: .iterator(); mappings.hasNext();) {
1185: final AttributeMapping am = (AttributeMapping) mappings
1186: .next();
1187: if (am == parentMapping) {
1188: continue;
1189: }
1190:
1191: final StepList targetXPath = am.getTargetXPath();
1192: boolean isChild = true;
1193: if (targetXPath.size() >= mvaluedPath.size()) {
1194: for (int currStepIdx = 0; currStepIdx < mvaluedPath
1195: .size(); currStepIdx++) {
1196: XPath.Step parentStep = (XPath.Step) mvaluedPath
1197: .get(currStepIdx);
1198: XPath.Step childStep = (XPath.Step) targetXPath
1199: .get(currStepIdx);
1200: QName parentStepName = parentStep.getName();
1201: QName childStepName = childStep.getName();
1202: if (!parentStepName.equals(childStepName)) {
1203: isChild = false;
1204: break;
1205: }
1206: }
1207: } else {
1208: isChild = false;
1209: }
1210: if (isChild) {
1211: singleValuedMappings.remove(am);
1212: childMappings.add(am);
1213: multiValuedSourcePropNames
1214: .addAll(getSourcePropertyNames(am));
1215: }
1216: }
1217: }
1218: }
1219:
1220: /**
1221: * Looks up and returns the property names from the source feature type
1222: * referenced by a given mapping
1223: *
1224: * @param am
1225: * @return
1226: */
1227: private Collection getSourcePropertyNames(AttributeMapping am) {
1228: Set sourceAttNames = new HashSet();
1229: FilterAttributeExtractor attExtractor = new FilterAttributeExtractor();
1230:
1231: am.getIdentifierExpression().accept(attExtractor, null);
1232: sourceAttNames.addAll(attExtractor.getAttributeNameSet());
1233:
1234: am.getSourceExpression().accept(attExtractor, null);
1235: sourceAttNames.addAll(attExtractor.getAttributeNameSet());
1236:
1237: // Rob A: add attribute (clientProperty) values here too!
1238: Map cp = am.getClientProperties();
1239:
1240: for (Iterator it = cp.entrySet().iterator(); it.hasNext();) {
1241: Entry entry = (Entry) it.next();
1242: // Name attName = (Name) entry.getKey();
1243: Object expr = entry.getValue();
1244: if (expr instanceof List)
1245: expr = (Object) ((List) expr).get(0);
1246:
1247: if (expr instanceof Expression) {
1248: ((Expression) expr).accept(attExtractor, null);
1249: // now should parse it as an expression and extract any property names
1250: sourceAttNames.addAll(attExtractor
1251: .getAttributeNameSet());
1252: }
1253: }
1254:
1255: return sourceAttNames;
1256: }
1257:
1258: /**
1259: * Takes a Query and returns another one ensuring that all the grouping
1260: * attributes are requested, in order to be able of producing the correct
1261: * number of output features, for example, from a joined set of tables.
1262: *
1263: * @param query
1264: * @return
1265: */
1266: private Query ensureGroupingAttsPresent(Query query) {
1267: if (query.retrieveAllProperties()) {
1268: return query;
1269: }
1270:
1271: List groupByAttributeNames = super .mapping.getGroupByAttNames();
1272: DefaultQuery neededQuery = new DefaultQuery(query);
1273: List requestedAtts = Arrays.asList(query.getPropertyNames());
1274: if (!requestedAtts.containsAll(groupByAttributeNames)) {
1275: List remaining = new ArrayList(groupByAttributeNames);
1276: remaining.removeAll(requestedAtts);
1277: LOGGER.fine("Adding missing grouping atts: " + remaining);
1278:
1279: List queryAtts = new ArrayList(remaining);
1280: queryAtts.addAll(requestedAtts);
1281:
1282: neededQuery.setPropertyNames(queryAtts);
1283: }
1284: return neededQuery;
1285: }
1286: }
|