001: package org.geotools.feature.iso;
002:
003: import java.util.AbstractList;
004: import java.util.ArrayList;
005: import java.util.Collection;
006: import java.util.Collections;
007: import java.util.Iterator;
008: import java.util.LinkedList;
009: import java.util.List;
010:
011: import org.geotools.resources.Utilities;
012: import org.opengis.feature.Association;
013: import org.opengis.feature.Attribute;
014: import org.opengis.feature.ComplexAttribute;
015: import org.opengis.feature.Property;
016: import org.opengis.feature.type.AttributeDescriptor;
017: import org.opengis.feature.type.AttributeType;
018: import org.opengis.feature.type.ComplexType;
019: import org.opengis.feature.type.Name;
020: import org.opengis.feature.type.PropertyDescriptor;
021:
022: public class ComplexAttributeImpl implements ComplexAttribute {
023:
024: private transient int HASHCODE = -1;
025:
026: protected AttributeDescriptor/* <ComplexType> */DESCRIPTOR;
027:
028: protected final ComplexType TYPE;
029:
030: protected final String ID;
031:
032: protected List properties;
033:
034: protected List/* <Attribute> */attributes; // TODO Collection (use
035: // properties.getClass().newInstancee()
036:
037: protected List/* <Association> */associations;
038:
039: private List/* <PropertyType> */types = null;
040:
041: private List/* <Object> */values = null;
042:
043: public ComplexAttributeImpl(Collection values, ComplexType type,
044: String id) {
045:
046: TYPE = type;
047: ID = id;
048:
049: properties = new ArrayList/* <Property> */();
050: setValue(values);
051: }
052:
053: public ComplexAttributeImpl(Collection values,
054: AttributeDescriptor desc, String id) {
055: this (values, (ComplexType) desc.type(), id);
056:
057: DESCRIPTOR = desc;
058: }
059:
060: public AttributeType getType() {
061: return TYPE;
062: }
063:
064: public AttributeDescriptor getDescriptor() {
065: return DESCRIPTOR;
066: }
067:
068: public PropertyDescriptor descriptor() {
069: return getDescriptor();
070: }
071:
072: public String getID() {
073: return ID;
074: }
075:
076: public Name name() {
077: return DESCRIPTOR != null ? DESCRIPTOR.getName() : null;
078: }
079:
080: public boolean nillable() {
081: if (getDescriptor() != null) {
082: return getDescriptor().isNillable();
083: }
084:
085: return true;
086: }
087:
088: public Object getValue() {
089: return Collections.unmodifiableList(properties);
090: }
091:
092: public Collection associations() {
093: if (associations == null) {
094: synchronized (this ) {
095: if (associations == null) {
096: associations = new ArrayList();
097: for (Iterator itr = properties.iterator(); itr
098: .hasNext();) {
099: Property property = (Property) itr.next();
100: if (property instanceof Association) {
101: associations.add(property);
102: }
103: }
104: }
105: }
106: }
107:
108: return Collections.unmodifiableList(associations);
109: }
110:
111: public Collection attributes() {
112: if (attributes == null) {
113: synchronized (this ) {
114: if (attributes == null) {
115: attributes = new ArrayList();
116: for (Iterator itr = properties.iterator(); itr
117: .hasNext();) {
118: Property property = (Property) itr.next();
119: if (property instanceof Attribute) {
120: attributes.add(property);
121: }
122: }
123: }
124: }
125: }
126:
127: return Collections.unmodifiableList(attributes);
128: }
129:
130: public List/* <Property> */get(Name name) {
131: // JD: this is a farily lenient check, should we be stricter about
132: // matching up the namespace
133: List/* <Property> */childs = new LinkedList/* <Property> */();
134:
135: for (Iterator itr = this .properties.iterator(); itr.hasNext();) {
136: Property prop = (Property) itr.next();
137: PropertyDescriptor node = prop.descriptor();
138: Name propName = node.getName();
139: if (name.getNamespaceURI() != null) {
140: if (propName.equals(name)) {
141: childs.add(prop);
142: }
143: } else {
144: // just do a local part compare
145: String localName = propName.getLocalPart();
146: if (localName.equals(name.getLocalPart())) {
147: childs.add(prop);
148: }
149: }
150:
151: }
152: return childs;
153: }
154:
155: /**
156: * Represents just enough info to convey the idea of this being a "view"
157: * into getAttribtues.
158: */
159: protected synchronized List/* <AttributeType> */types() {
160: if (types == null) {
161: types = createTypesView((List) getValue());
162: }
163: return types;
164: }
165:
166: /** Factory method so subclasses can optimize */
167: protected List/* <AttributeType> */createTypesView(
168: final List/* <Attribute> */source) {
169: if (source == null)
170: return Collections.EMPTY_LIST;
171:
172: return new AbstractList/* <AttributeType> */() {
173: // @Override
174: public Object /* AttributeType */get(int index) {
175: return ((Attribute) source.get(index)).getType();
176: }
177:
178: // @Override
179: public int size() {
180: return source.size();
181: }
182:
183: // @Override
184: public Object /* AttributeType */remove(int index) {
185: Attribute removed = (Attribute) source.remove(index);
186: if (removed != null) {
187: return removed.getType();
188: }
189: return null;
190: }
191:
192: /**
193: * Unsupported.
194: * <p>
195: * We may be able to do this for nilable types, or types that have a
196: * default value.
197: * </p>
198: *
199: * @param index
200: * @param type
201: */
202: // @Override
203: public void add(int index, Object o) {
204: throw new UnsupportedOperationException(
205: "Cannot add directly to types");
206: }
207: };
208: }
209:
210: public synchronized List/* <Object> */getValues() {
211: if (values == null) {
212: values = createValuesView((List) getValue());
213: }
214: return values;
215: }
216:
217: /** Factory method so subclasses can optimize */
218: protected List/* <Object> */createValuesView(
219: final List/* <Attribute> */source) {
220: return new AbstractList/* <Object> */() {
221: // @Override
222: public Object get(int index) {
223: return ((Attribute) source.get(index)).getValue();
224: }
225:
226: // @Override
227: public Object set(int index, Object value) {
228: Object replaced = ((Attribute) source.get(index))
229: .getValue();
230: ((Attribute) source.get(index)).setValue(value);
231: return replaced;
232: }
233:
234: // @Override
235: public int size() {
236: return source.size();
237: }
238:
239: // @Override
240: public Object /* AttributeType */remove(int index) {
241: Attribute removed = (Attribute) source.remove(index);
242: if (removed != null) {
243: return removed.getValue();
244: }
245: return null;
246: }
247:
248: /**
249: * Unsupported, we can support this for flat schema.
250: * <p>
251: * We may be able to do this after walking the schema and figuring
252: * out that there is only one binding for the provided object.
253: * </p>
254: *
255: * @param index
256: * @param testType
257: */
258: // @Override
259: public void add(int index, Object value) {
260: throw new UnsupportedOperationException(
261: "Cannot add directly to values");
262: }
263: };
264: }
265:
266: public void setValue(Object newValue) {
267:
268: if (newValue == null) {
269: properties.clear();
270: } else {
271: properties = new ArrayList((Collection) newValue);
272: }
273:
274: // reset "views"
275: attributes = null;
276: associations = null;
277: types = null;
278: values = null;
279: }
280:
281: protected Object get(AttributeType type) {
282: if (type == null) {
283: throw new NullPointerException("type");
284: }
285:
286: // JD: Is this crazy or is it just me? This method returns an object
287: // in one case, and collection in the other?
288: ComplexType ctype = TYPE;
289: if (Descriptors.multiple(ctype, type)) {
290: List/* <Object> */got = new ArrayList/* <Object> */();
291: for (Iterator itr = properties.iterator(); itr.hasNext();) {
292: Attribute attribute = (Attribute) itr.next();
293: if (attribute.getType().equals(type)) {
294: got.add(attribute.getValue());
295: }
296: }
297: return got;
298: } else {
299: for (Iterator itr = properties.iterator(); itr.hasNext();) {
300: Attribute attribute = (Attribute) itr.next();
301: if (attribute.getType().equals(type)) {
302: return attribute.getValue();
303: }
304: }
305: return null;
306: }
307: }
308:
309: public boolean equals(Object o) {
310: if (!(o instanceof ComplexAttributeImpl)) {
311: return false;
312: }
313: ComplexAttributeImpl c = (ComplexAttributeImpl) o;
314:
315: if (!Utilities.equals(ID, c.ID))
316: return false;
317:
318: if (!Utilities.equals(TYPE, c.TYPE))
319: return false;
320:
321: if (!Utilities.equals(DESCRIPTOR, c.DESCRIPTOR)) {
322: return false;
323: }
324:
325: return this .properties.equals(c.properties);
326: }
327:
328: public int hashCode() {
329: if (HASHCODE == -1) {
330: HASHCODE = 23 + (TYPE == null ? 1 : TYPE.hashCode())
331: * (DESCRIPTOR == null ? 1 : DESCRIPTOR.hashCode())
332: * properties.hashCode()
333: * (ID == null ? 1 : ID.hashCode());
334: }
335: return HASHCODE;
336: }
337:
338: public String toString() {
339: StringBuffer sb = new StringBuffer(getClass().getName());
340: List/* <Attribute> */atts = this .properties;
341: sb.append("[id=").append(this .ID).append(", name=").append(
342: DESCRIPTOR != null ? DESCRIPTOR.getName().toString()
343: : "null").append(", type=").append(
344: getType().getName()).append('\n');
345: for (Iterator itr = atts.iterator(); itr.hasNext();) {
346: Attribute att = (Attribute) itr.next();
347: sb.append(att);
348: sb.append('\n');
349: }
350: sb.append("]");
351: return sb.toString();
352: }
353:
354: public Object operation(Name arg0, List arg1) {
355: throw new UnsupportedOperationException(
356: "operation not supported yet");
357: }
358:
359: }
|