001: /*
002: * Copyright (C) 2005 Joe Walnes.
003: * Copyright (C) 2006, 2007 XStream Committers.
004: * All rights reserved.
005: *
006: * The software in this package is published under the terms of the BSD
007: * style license a copy of which has been included with this distribution in
008: * the LICENSE.txt file.
009: *
010: * Created on 16. February 2005 by Joe Walnes
011: */
012: package com.thoughtworks.xstream.mapper;
013:
014: import com.thoughtworks.xstream.InitializationException;
015: import com.thoughtworks.xstream.alias.ClassMapper;
016:
017: import java.lang.reflect.Field;
018: import java.util.Collection;
019: import java.util.HashMap;
020: import java.util.Iterator;
021: import java.util.Map;
022:
023: public class ImplicitCollectionMapper extends MapperWrapper {
024:
025: public ImplicitCollectionMapper(Mapper wrapped) {
026: super (wrapped);
027: }
028:
029: /**
030: * @deprecated As of 1.2, use {@link #ImplicitCollectionMapper(Mapper)}
031: */
032: public ImplicitCollectionMapper(ClassMapper wrapped) {
033: this ((Mapper) wrapped);
034: }
035:
036: // { definedIn (Class) -> (ImplicitCollectionMapperForClass) }
037: private final Map classNameToMapper = new HashMap();
038:
039: private ImplicitCollectionMapperForClass getMapper(Class definedIn) {
040: while (definedIn != null) {
041: ImplicitCollectionMapperForClass mapper = (ImplicitCollectionMapperForClass) classNameToMapper
042: .get(definedIn);
043: if (mapper != null) {
044: return mapper;
045: }
046: definedIn = definedIn.getSuperclass();
047: }
048: return null;
049: }
050:
051: private ImplicitCollectionMapperForClass getOrCreateMapper(
052: Class definedIn) {
053: ImplicitCollectionMapperForClass mapper = getMapper(definedIn);
054: if (mapper == null) {
055: mapper = new ImplicitCollectionMapperForClass();
056: classNameToMapper.put(definedIn, mapper);
057: }
058: return mapper;
059: }
060:
061: public String getFieldNameForItemTypeAndName(Class definedIn,
062: Class itemType, String itemFieldName) {
063: ImplicitCollectionMapperForClass mapper = getMapper(definedIn);
064: if (mapper != null) {
065: return mapper.getFieldNameForItemTypeAndName(itemType,
066: itemFieldName);
067: } else {
068: return null;
069: }
070: }
071:
072: public Class getItemTypeForItemFieldName(Class definedIn,
073: String itemFieldName) {
074: ImplicitCollectionMapperForClass mapper = getMapper(definedIn);
075: if (mapper != null) {
076: return mapper.getItemTypeForItemFieldName(itemFieldName);
077: } else {
078: return null;
079: }
080: }
081:
082: public ImplicitCollectionMapping getImplicitCollectionDefForFieldName(
083: Class itemType, String fieldName) {
084: ImplicitCollectionMapperForClass mapper = getMapper(itemType);
085: if (mapper != null) {
086: return mapper
087: .getImplicitCollectionDefForFieldName(fieldName);
088: } else {
089: return null;
090: }
091: }
092:
093: public void add(Class definedIn, String fieldName, Class itemType) {
094: add(definedIn, fieldName, null, itemType);
095: }
096:
097: public void add(Class definedIn, String fieldName,
098: String itemFieldName, Class itemType) {
099: Field field = null;
100: while (definedIn != Object.class) {
101: try {
102: field = definedIn.getDeclaredField(fieldName);
103: break;
104: } catch (SecurityException e) {
105: throw new InitializationException(
106: "Access denied for field with implicit collection",
107: e);
108: } catch (NoSuchFieldException e) {
109: definedIn = definedIn.getSuperclass();
110: }
111: }
112: if (field == null) {
113: throw new InitializationException("No field \"" + fieldName
114: + "\" for implicit collection");
115: } else if (!Collection.class.isAssignableFrom(field.getType())) {
116: throw new InitializationException("Field \"" + fieldName
117: + "\" declares no collection");
118: }
119: ImplicitCollectionMapperForClass mapper = getOrCreateMapper(definedIn);
120: mapper.add(new ImplicitCollectionMappingImpl(fieldName,
121: itemType, itemFieldName));
122: }
123:
124: private static class ImplicitCollectionMapperForClass {
125: // { (NamedItemType) -> (ImplicitCollectionDefImpl) }
126: private Map namedItemTypeToDef = new HashMap();
127: // { itemFieldName (String) -> (ImplicitCollectionDefImpl) }
128: private Map itemFieldNameToDef = new HashMap();
129: // { fieldName (String) -> (ImplicitCollectionDefImpl) }
130: private Map fieldNameToDef = new HashMap();
131:
132: public String getFieldNameForItemTypeAndName(Class itemType,
133: String itemFieldName) {
134: ImplicitCollectionMappingImpl unnamed = null;
135: for (Iterator iterator = namedItemTypeToDef.keySet()
136: .iterator(); iterator.hasNext();) {
137: NamedItemType itemTypeForFieldName = (NamedItemType) iterator
138: .next();
139: if (itemTypeForFieldName.itemType
140: .isAssignableFrom(itemType)) {
141: ImplicitCollectionMappingImpl def = (ImplicitCollectionMappingImpl) namedItemTypeToDef
142: .get(itemTypeForFieldName);
143: if (def.getItemFieldName() != null) {
144: if (def.getItemFieldName()
145: .equals(itemFieldName)) {
146: return def.getFieldName();
147: }
148: } else {
149: unnamed = def;
150: if (itemFieldName == null) {
151: break;
152: }
153: }
154: }
155: }
156: return unnamed != null ? unnamed.getFieldName() : null;
157: }
158:
159: public Class getItemTypeForItemFieldName(String itemFieldName) {
160: ImplicitCollectionMappingImpl def = getImplicitCollectionDefByItemFieldName(itemFieldName);
161: if (def != null) {
162: return def.getItemType();
163: } else {
164: return null;
165: }
166: }
167:
168: private ImplicitCollectionMappingImpl getImplicitCollectionDefByItemFieldName(
169: String itemFieldName) {
170: if (itemFieldName == null) {
171: return null;
172: } else {
173: return (ImplicitCollectionMappingImpl) itemFieldNameToDef
174: .get(itemFieldName);
175: }
176: }
177:
178: public ImplicitCollectionMappingImpl getImplicitCollectionDefByFieldName(
179: String fieldName) {
180: return (ImplicitCollectionMappingImpl) fieldNameToDef
181: .get(fieldName);
182: }
183:
184: public ImplicitCollectionMapping getImplicitCollectionDefForFieldName(
185: String fieldName) {
186: return (ImplicitCollectionMapping) fieldNameToDef
187: .get(fieldName);
188: }
189:
190: public void add(ImplicitCollectionMappingImpl def) {
191: fieldNameToDef.put(def.getFieldName(), def);
192: namedItemTypeToDef.put(def.createNamedItemType(), def);
193: if (def.getItemFieldName() != null) {
194: itemFieldNameToDef.put(def.getItemFieldName(), def);
195: }
196: }
197:
198: }
199:
200: private static class ImplicitCollectionMappingImpl implements
201: ImplicitCollectionMapping {
202: private String fieldName;
203: private String itemFieldName;
204: private Class itemType;
205:
206: ImplicitCollectionMappingImpl(String fieldName, Class itemType,
207: String itemFieldName) {
208: this .fieldName = fieldName;
209: this .itemFieldName = itemFieldName;
210: this .itemType = itemType == null ? Object.class : itemType;
211: }
212:
213: public boolean equals(Object obj) {
214: if (obj instanceof ImplicitCollectionMappingImpl) {
215: ImplicitCollectionMappingImpl b = (ImplicitCollectionMappingImpl) obj;
216: return fieldName.equals(b.fieldName)
217: && isEquals(itemFieldName, b.itemFieldName);
218: } else {
219: return false;
220: }
221: }
222:
223: public NamedItemType createNamedItemType() {
224: return new NamedItemType(itemType, itemFieldName);
225: }
226:
227: private static boolean isEquals(Object a, Object b) {
228: if (a == null) {
229: return b == null;
230: } else {
231: return a.equals(b);
232: }
233: }
234:
235: public int hashCode() {
236: int hash = fieldName.hashCode();
237: if (itemFieldName != null) {
238: hash += itemFieldName.hashCode() << 7;
239: }
240: return hash;
241: }
242:
243: public String getFieldName() {
244: return fieldName;
245: }
246:
247: public String getItemFieldName() {
248: return itemFieldName;
249: }
250:
251: public Class getItemType() {
252: return itemType;
253: }
254: }
255:
256: private static class NamedItemType {
257: Class itemType;
258: String itemFieldName;
259:
260: NamedItemType(Class itemType, String itemFieldName) {
261: this .itemType = itemType;
262: this .itemFieldName = itemFieldName;
263: }
264:
265: public boolean equals(Object obj) {
266: if (obj instanceof NamedItemType) {
267: NamedItemType b = (NamedItemType) obj;
268: return itemType.equals(b.itemType)
269: && isEquals(itemFieldName, b.itemFieldName);
270: } else {
271: return false;
272: }
273: }
274:
275: private static boolean isEquals(Object a, Object b) {
276: if (a == null) {
277: return b == null;
278: } else {
279: return a.equals(b);
280: }
281: }
282:
283: public int hashCode() {
284: int hash = itemType.hashCode() << 7;
285: if (itemFieldName != null) {
286: hash += itemFieldName.hashCode();
287: }
288: return hash;
289: }
290: }
291: }
|