001: /*
002: * hgcommons 7
003: * Hammurapi Group Common Library
004: * Copyright (C) 2003 Hammurapi Group
005: *
006: * This program is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation; either
009: * version 2 of the License, or (at your option) any later version.
010: *
011: * This program is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: *
016: * You should have received a copy of the GNU Lesser General Public
017: * License along with this library; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
019: *
020: * URL: http://www.hammurapi.biz/hammurapi-biz/ef/xmenu/hammurapi-group/products/products/hgcommons/index.html
021: * e-Mail: support@hammurapi.biz
022: */
023: package biz.hammurapi.xml.dom;
024:
025: import java.io.IOException;
026: import java.lang.reflect.InvocationTargetException;
027: import java.lang.reflect.Method;
028: import java.util.ArrayList;
029: import java.util.Collection;
030: import java.util.HashMap;
031: import java.util.Iterator;
032: import java.util.Map;
033: import java.util.Stack;
034:
035: import biz.hammurapi.RuntimeException;
036: import biz.hammurapi.config.ConfigurationException;
037: import biz.hammurapi.config.DomConfigFactory;
038: import biz.hammurapi.util.ClassHierarchyVisitable;
039: import biz.hammurapi.util.Visitor;
040:
041: /**
042: * @author Pavel Vlasov
043: *
044: * @version $Revision: 1.8 $
045: */
046: public class CompositeDomSerializer {
047: public interface Member {
048: /**
049: * Callback method
050: * @param owner
051: */
052: void setOwner(CompositeDomSerializer owner);
053: }
054:
055: private Collection serializers = new ArrayList();
056:
057: /**
058: * Serializer which to delegate serialization to
059: * if this serializer doesn't containt appropriate
060: * means to serialize given object.
061: */
062: protected CompositeDomSerializer upInStack;
063:
064: /**
065: * Serializer to delegate requests for serialization from members.
066: */
067: protected CompositeDomSerializer downInStack;
068:
069: /**
070: * For use by members.
071: * @return CompositeDomSerializer at stack's head to delegate requests
072: * from members to.
073: */
074: public CompositeDomSerializer getStackHead() {
075: return (downInStack == null || downInStack == this ) ? this
076: : downInStack.getStackHead();
077: }
078:
079: /**
080: * Creates serializer prepopulated with standard serializers
081: * loaded from com/pavelvlasov/xml/dom/CompositeDomSerializer.xml resource.
082: */
083: public CompositeDomSerializer() {
084: synchronized (standardSerializers) {
085: Iterator it = standardSerializers.iterator();
086: while (it.hasNext()) {
087: addDomSerializer(it.next());
088: }
089: }
090: }
091:
092: /**
093: * Creates serializer populated with given collection of serializers
094: * @param serializers
095: */
096: public CompositeDomSerializer(Collection serializers) {
097: Iterator it = serializers.iterator();
098: while (it.hasNext()) {
099: addDomSerializer(it.next());
100: }
101: }
102:
103: private interface DomSerializer {
104: Class getSourceClass();
105:
106: DomSerializable toDomSerializable(Object o);
107: }
108:
109: public void addDomSerializer(final Object ds) {
110: if (ds instanceof Member) {
111: ((Member) ds).setOwner(this );
112: }
113:
114: final Class clazz = ds.getClass();
115: Method[] ma = clazz.getMethods();
116:
117: for (int i = 0; i < ma.length; i++) {
118: if ("toDomSerializable".equals(ma[i].getName())
119: && DomSerializable.class.isAssignableFrom(ma[i]
120: .getReturnType())
121: && ma[i].getParameterTypes().length == 1) {
122:
123: final Method m = ma[i];
124: serializers.add(new DomSerializer() {
125:
126: public Class getSourceClass() {
127: return m.getParameterTypes()[0];
128: }
129:
130: public DomSerializable toDomSerializable(Object o) {
131: try {
132: return (DomSerializable) m.invoke(ds,
133: new Object[] { o });
134: } catch (IllegalAccessException e) {
135: throw new RuntimeException(e);
136: } catch (InvocationTargetException e) {
137: throw new RuntimeException(e);
138: }
139: }
140: });
141:
142: }
143: }
144: }
145:
146: private Map serializerMap = new HashMap();
147:
148: public DomSerializable toDomSerializable(Object object) {
149: if (object == null) {
150: return null;
151: }
152:
153: if (object instanceof DomSerializable) {
154: return (DomSerializable) object;
155: }
156:
157: DomSerializer serializer;
158: synchronized (serializerMap) {
159: serializer = (DomSerializer) serializerMap.get(object
160: .getClass().getName());
161: if (serializer == null) {
162: int affinity = Integer.MAX_VALUE;
163: Iterator it = serializers.iterator();
164: while (it.hasNext()) {
165: final DomSerializer ds = (DomSerializer) it.next();
166: if (ds.getSourceClass().isInstance(object)
167: && ds.getSourceClass().isArray() == object
168: .getClass().isArray()) {
169: final int[] caffinity = { 0 };
170: new ClassHierarchyVisitable(object.getClass())
171: .accept(new Visitor() {
172:
173: public boolean visit(Object target) {
174: if (target.equals(ds
175: .getSourceClass())) {
176: return false;
177: }
178:
179: caffinity[0]++;
180: return true;
181: }
182:
183: });
184:
185: // System.out.println(object.getClass()+" - "+ds.getSourceClass()+" : "+caffinity[0]);
186:
187: if (serializer == null
188: || caffinity[0] < affinity) {
189: serializer = ds;
190: affinity = caffinity[0];
191: }
192: }
193: }
194:
195: if (serializer != null) {
196: serializerMap.put(object.getClass().getName(),
197: serializer);
198: }
199: }
200: }
201:
202: if (serializer == null) {
203: return upInStack == null ? null : upInStack
204: .toDomSerializable(object);
205: }
206:
207: return serializer.toDomSerializable(object);
208: }
209:
210: private static CompositeDomSerializer defaultInstance;
211:
212: private static Collection standardSerializers;
213:
214: static {
215: try {
216: DomConfigFactory factory = new DomConfigFactory();
217: standardSerializers = (Collection) factory
218: .create(
219: CompositeDomSerializer.class
220: .getResourceAsStream("CompositeDomSerializer.xml"),
221: null);
222: } catch (ConfigurationException e) {
223: throw new ExceptionInInitializerError(e);
224: } catch (IOException e) {
225: throw new ExceptionInInitializerError(e);
226: }
227: defaultInstance = new CompositeDomSerializer();
228: if (defaultInstance.serializers.isEmpty()) {
229: throw new IllegalStateException(
230: "Default composite serializer doesn't contain any serializers.");
231: }
232: }
233:
234: private static ThreadLocal threadSerializerTL = new ThreadLocal() {
235: protected Object initialValue() {
236: return new Stack();
237: }
238: };
239:
240: public static CompositeDomSerializer getThreadInstance() {
241: Stack stack = (Stack) threadSerializerTL.get();
242:
243: if (stack.isEmpty()) {
244: return defaultInstance;
245: }
246:
247: return (CompositeDomSerializer) stack.peek();
248: }
249:
250: public static void pushThreadSerializer(
251: CompositeDomSerializer threadSerializer) {
252: if (threadSerializer != null) {
253: CompositeDomSerializer cti = getThreadInstance();
254: if (cti != threadSerializer) {
255: threadSerializer.upInStack = cti;
256: cti.downInStack = threadSerializer;
257: ((Stack) threadSerializerTL.get())
258: .push(threadSerializer);
259: }
260: }
261: }
262:
263: public static CompositeDomSerializer popThreadSerializer() {
264: Stack stack = (Stack) threadSerializerTL.get();
265: if (stack.isEmpty()) {
266: return null;
267: }
268:
269: CompositeDomSerializer ret = (CompositeDomSerializer) stack
270: .pop();
271: if (ret.upInStack != null) {
272: ret.upInStack.downInStack = null;
273: ret.upInStack = null;
274: }
275: return ret;
276: }
277:
278: }
|