001: /*
002: * Copyright 2005 Joe Walker
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package org.directwebremoting.convert;
017:
018: import java.lang.reflect.Modifier;
019: import java.util.ArrayList;
020: import java.util.Collection;
021: import java.util.HashSet;
022: import java.util.Iterator;
023: import java.util.List;
024: import java.util.Set;
025: import java.util.SortedSet;
026: import java.util.StringTokenizer;
027: import java.util.TreeSet;
028:
029: import org.apache.commons.logging.Log;
030: import org.apache.commons.logging.LogFactory;
031: import org.directwebremoting.dwrp.ArrayJsonOutboundVariable;
032: import org.directwebremoting.dwrp.ArrayNonJsonOutboundVariable;
033: import org.directwebremoting.dwrp.CollectionOutboundVariable;
034: import org.directwebremoting.dwrp.ErrorOutboundVariable;
035: import org.directwebremoting.dwrp.ParseUtil;
036: import org.directwebremoting.dwrp.ProtocolConstants;
037: import org.directwebremoting.extend.Converter;
038: import org.directwebremoting.extend.ConverterManager;
039: import org.directwebremoting.extend.InboundContext;
040: import org.directwebremoting.extend.InboundVariable;
041: import org.directwebremoting.extend.MarshallException;
042: import org.directwebremoting.extend.OutboundContext;
043: import org.directwebremoting.extend.OutboundVariable;
044: import org.directwebremoting.extend.TypeHintContext;
045: import org.directwebremoting.util.LocalUtil;
046: import org.directwebremoting.util.Messages;
047:
048: /**
049: * An implementation of Converter for Collections of Strings.
050: * @author Joe Walker [joe at getahead dot ltd dot uk]
051: */
052: public class CollectionConverter extends BaseV20Converter implements
053: Converter {
054: /* (non-Javadoc)
055: * @see org.directwebremoting.convert.BaseV20Converter#setConverterManager(org.directwebremoting.ConverterManager)
056: */
057: @Override
058: public void setConverterManager(ConverterManager converterManager) {
059: this .converterManager = converterManager;
060: }
061:
062: /* (non-Javadoc)
063: * @see org.directwebremoting.Converter#convertInbound(java.lang.Class, org.directwebremoting.InboundVariable, org.directwebremoting.InboundContext)
064: */
065: @SuppressWarnings("unchecked")
066: public Object convertInbound(Class<?> paramType,
067: InboundVariable data, InboundContext inctx)
068: throws MarshallException {
069: String value = data.getValue();
070:
071: // If the text is null then the whole bean is null
072: if (value.trim().equals(ProtocolConstants.INBOUND_NULL)) {
073: return null;
074: }
075:
076: if (!value.startsWith(ProtocolConstants.INBOUND_ARRAY_START)) {
077: throw new MarshallException(paramType, Messages.getString(
078: "CollectionConverter.FormatError",
079: ProtocolConstants.INBOUND_ARRAY_START));
080: }
081:
082: if (!value.endsWith(ProtocolConstants.INBOUND_ARRAY_END)) {
083: throw new MarshallException(paramType, Messages.getString(
084: "CollectionConverter.FormatError",
085: ProtocolConstants.INBOUND_ARRAY_END));
086: }
087:
088: value = value.substring(1, value.length() - 1);
089:
090: try {
091: TypeHintContext icc = inctx.getCurrentTypeHintContext();
092:
093: TypeHintContext subthc = icc.createChildContext(0);
094: Class<?> subtype = subthc.getExtraTypeInfo();
095:
096: // subtype.getMethod("h", null).getTypeParameters();
097: Collection<Object> col;
098:
099: // If they want an iterator then just use an array list and fudge
100: // at the end.
101: if (Iterator.class.isAssignableFrom(paramType)) {
102: col = new ArrayList<Object>();
103: }
104: // If paramType is concrete then just use whatever we've got.
105: else if (!paramType.isInterface()
106: && !Modifier.isAbstract(paramType.getModifiers())) {
107: // If there is a problem creating the type then we have no way
108: // of completing this - they asked for a specific type and we
109: // can't create that type. I don't know of a way of finding
110: // subclasses that might be instaniable so we accept failure.
111: //noinspection unchecked
112: col = (Collection<Object>) paramType.newInstance();
113: }
114: // If they want a SortedSet then use TreeSet
115: else if (SortedSet.class.isAssignableFrom(paramType)) {
116: col = new TreeSet<Object>();
117: }
118: // If they want a Set then use HashSet
119: else if (Set.class.isAssignableFrom(paramType)) {
120: col = new HashSet<Object>();
121: }
122: // If they want a List then use an ArrayList
123: else if (List.class.isAssignableFrom(paramType)) {
124: col = new ArrayList<Object>();
125: }
126: // If they just want a Collection then just use an ArrayList
127: else if (Collection.class.isAssignableFrom(paramType)) {
128: col = new ArrayList<Object>();
129: } else {
130: throw new MarshallException(paramType);
131: }
132:
133: // We should put the new object into the working map in case it
134: // is referenced later nested down in the conversion process.
135: inctx.addConverted(data, paramType, col);
136:
137: StringTokenizer st = new StringTokenizer(value,
138: ProtocolConstants.INBOUND_ARRAY_SEPARATOR);
139: int size = st.countTokens();
140: for (int i = 0; i < size; i++) {
141: String token = st.nextToken();
142:
143: String[] split = ParseUtil.splitInbound(token);
144: String splitType = split[LocalUtil.INBOUND_INDEX_TYPE];
145: String splitValue = split[LocalUtil.INBOUND_INDEX_VALUE];
146:
147: InboundVariable nested = new InboundVariable(data
148: .getLookup(), null, splitType, splitValue);
149: nested.dereference();
150:
151: Object output = converterManager.convertInbound(
152: subtype, nested, inctx, subthc);
153: col.add(output);
154: }
155:
156: // If they wanted an Iterator then give them one otherwise use
157: // the type we created
158: if (Iterator.class.isAssignableFrom(paramType)) {
159: return col.iterator();
160: } else {
161: return col;
162: }
163: } catch (Exception ex) {
164: throw new MarshallException(paramType, ex);
165: }
166: }
167:
168: /* (non-Javadoc)
169: * @see org.directwebremoting.Converter#convertOutbound(java.lang.Object, org.directwebremoting.OutboundContext)
170: */
171: @SuppressWarnings("unchecked")
172: public OutboundVariable convertOutbound(Object data,
173: OutboundContext outctx) throws MarshallException {
174: // First we need to get ourselves the collection data
175: Iterator<Object> it;
176: if (data instanceof Collection) {
177: Collection<Object> col = (Collection<Object>) data;
178: it = col.iterator();
179: } else if (data instanceof Iterator) {
180: it = (Iterator<Object>) data;
181: } else {
182: throw new MarshallException(data.getClass());
183: }
184:
185: // Stash this bit of data to cope with recursion
186: CollectionOutboundVariable ov;
187: if (outctx.isJsonMode()) {
188: ov = new ArrayJsonOutboundVariable();
189: } else {
190: ov = new ArrayNonJsonOutboundVariable(outctx);
191: }
192: outctx.put(data, ov);
193:
194: // Convert all the data members
195: List<OutboundVariable> ovs = new ArrayList<OutboundVariable>();
196: while (it.hasNext()) {
197: Object member = it.next();
198: OutboundVariable nested;
199:
200: try {
201: nested = converterManager.convertOutbound(member,
202: outctx);
203: } catch (Exception ex) {
204: String errorMessage = "Conversion error for "
205: + data.getClass().getName() + ".";
206: log.warn(errorMessage, ex);
207:
208: nested = new ErrorOutboundVariable(errorMessage);
209: }
210:
211: ovs.add(nested);
212: }
213:
214: // Group the list of converted objects into this OutboundVariable
215: ov.setChildren(ovs);
216:
217: return ov;
218: }
219:
220: /**
221: * For nested conversions
222: */
223: private ConverterManager converterManager = null;
224:
225: /**
226: * The log stream
227: */
228: private static final Log log = LogFactory
229: .getLog(CollectionConverter.class);
230: }
|