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.extend;
017:
018: import java.util.HashMap;
019: import java.util.Iterator;
020: import java.util.LinkedList;
021: import java.util.Map;
022:
023: import org.apache.commons.logging.LogFactory;
024: import org.apache.commons.logging.Log;
025: import org.directwebremoting.dwrp.ProtocolConstants;
026:
027: /**
028: * InboundContext is the context for set of inbound conversions.
029: * Since a data set may be recursive parts of some data members may refer to
030: * others so we need to keep track of who is converted for what.
031: * @author Joe Walker [joe at getahead dot ltd dot uk]
032: */
033: public final class InboundContext {
034: /**
035: * When we are sure we have finished parsing the input, we can begin to
036: * fix all cross-references.
037: * @throws MarshallException If cross-references don't add up
038: */
039: public void dereference() throws MarshallException {
040: for (InboundVariable variable : variables.values()) {
041: variable.dereference();
042: }
043: }
044:
045: /**
046: * Someone wants to tell us about a new conversion context.
047: * @param context The current conversion context
048: */
049: public void pushContext(TypeHintContext context) {
050: typeHintStack.addFirst(context);
051: }
052:
053: /**
054: * Someone wants to tell us about a finished conversion context.
055: */
056: public void popContext() {
057: typeHintStack.removeFirst();
058: }
059:
060: /**
061: * @return The method that we are currently converting data for
062: */
063: public TypeHintContext getCurrentTypeHintContext() {
064: return typeHintStack.getFirst();
065: }
066:
067: /**
068: * Create an inbound variable.
069: * Usually called by a query parser to setup a list of known variables.
070: * This method also checks to see if the new variable is a parameter and if
071: * it is it updates the count of parameters
072: * @param callNum The call number to work on
073: * @param key The name of the variable
074: * @param type The javascript type of the variable
075: * @param value The value of the variable
076: */
077: public void createInboundVariable(int callNum, String key,
078: String type, String value) {
079: InboundVariable cte = new InboundVariable(this , key, type,
080: value);
081: checkInboundVariable(callNum, key, cte);
082: }
083:
084: /**
085: * Create an inbound file variable.
086: * Usually called by a query parser to setup a list of known variables.
087: * This method also checks to see if the new variable is a parameter and if
088: * it is it updates the count of parameters
089: * @param callNum The call number to work on
090: * @param key The name of the variable
091: * @param type The javascript type of the variable
092: * @param value The value of the file
093: */
094: public void createInboundVariable(int callNum, String key,
095: String type, FormField value) {
096: InboundVariable iv = new InboundVariable(this , key, type, value);
097: checkInboundVariable(callNum, key, iv);
098: }
099:
100: /**
101: * Internal method to check the variable we just created does not already
102: * exist, and to ensure that our count of inbound parameters is up to date
103: * @param callNum The number of this call
104: * @param key The name of the variable
105: * @param iv The value to check
106: */
107: private void checkInboundVariable(int callNum, String key,
108: InboundVariable iv) {
109: Object old = variables.put(key, iv);
110: if (old != null) {
111: log.warn("Duplicate variable called: " + key);
112: }
113:
114: String paramPrefix = ProtocolConstants.INBOUND_CALLNUM_PREFIX
115: + callNum + ProtocolConstants.INBOUND_CALLNUM_SUFFIX
116: + ProtocolConstants.INBOUND_KEY_PARAM;
117:
118: if (key.startsWith(paramPrefix)) {
119: int i = Integer.parseInt(key
120: .substring(paramPrefix.length())) + 1;
121: if (i > paramCount) {
122: paramCount = i;
123: }
124: }
125: }
126:
127: /**
128: * Internal method to allow entries to resolve references
129: * @param name The name of the variable to lookup
130: * @return The found variable
131: */
132: public InboundVariable getInboundVariable(String name) {
133: return variables.get(name);
134: }
135:
136: /**
137: * Clear the list of converted objects.
138: * If the conversion attempt for a given method failed, we may want to try
139: * another so we will need to ditch the list of converted objects because
140: * the next method could well have different parameter types.
141: */
142: public void clearConverted() {
143: converted.clear();
144: }
145:
146: /**
147: * Add to the (temporary) list of converted objects
148: * @param iv The converted object
149: * @param type The type that we converted the object to
150: * @param bean The converted version
151: */
152: public void addConverted(InboundVariable iv, Class<?> type,
153: Object bean) {
154: Conversion conversion = new Conversion(iv, type);
155: Object old = converted.put(conversion, bean);
156: if (old != null) {
157: log.warn("Duplicate variable conversion called: "
158: + conversion);
159: }
160: }
161:
162: /**
163: * Check to see if the conversion has already been done
164: * @param iv The inbound data to check
165: * @param type The type that we want the object converted to
166: * @return The converted data or null if it has not been converted
167: */
168: public Object getConverted(InboundVariable iv, Class<?> type) {
169: Conversion conversion = new Conversion(iv, type);
170: return converted.get(conversion);
171: }
172:
173: /**
174: * How many parameters are there?
175: * @return The parameter count
176: */
177: public int getParameterCount() {
178: return paramCount;
179: }
180:
181: /**
182: * This is a bit of a hack, needed for debug purposes - it counts the
183: * parameters (including method and script params) for a given call number
184: * @param callNum The Call number to count the parameters of
185: * @return The parameter count for a given Call
186: */
187: public int getParameterCount(int callNum) {
188: int count = 0;
189: String prefix = ProtocolConstants.INBOUND_CALLNUM_PREFIX
190: + callNum + ProtocolConstants.INBOUND_CALLNUM_SUFFIX
191: + ProtocolConstants.INBOUND_KEY_PARAM;
192: for (String key : variables.keySet()) {
193: if (key.startsWith(prefix)) {
194: count++;
195: }
196: }
197: return count;
198: }
199:
200: /**
201: * Get a parameter by index
202: * @param callNum The call number to work on
203: * @param index The parameter index
204: * @return The found parameter
205: */
206: public InboundVariable getParameter(int callNum, int index) {
207: String key = ProtocolConstants.INBOUND_CALLNUM_PREFIX + callNum
208: + ProtocolConstants.INBOUND_CALLNUM_SUFFIX
209: + ProtocolConstants.INBOUND_KEY_PARAM + index;
210:
211: return variables.get(key);
212: }
213:
214: /**
215: * A debug method so people can get a list of all the variable names
216: * @return an iterator over the known variable names
217: */
218: public Iterator<String> getInboundVariableNames() {
219: return variables.keySet().iterator();
220: }
221:
222: /**
223: * A Class to use as a key in a map for conversion purposes.
224: * A collection of an InboundVariable and a type
225: */
226: protected static class Conversion {
227: /**
228: * @param inboundVariable The new inboundVariable
229: * @param type The new type
230: */
231: Conversion(InboundVariable inboundVariable, Class<?> type) {
232: if (inboundVariable == null) {
233: throw new NullPointerException("InboundVariable");
234: }
235:
236: if (type == null) {
237: throw new NullPointerException("Class type");
238: }
239:
240: this .inboundVariable = inboundVariable;
241: this .type = type;
242: }
243:
244: /* (non-Javadoc)
245: * @see java.lang.Object#equals(java.lang.Object)
246: */
247: @Override
248: public boolean equals(Object obj) {
249: if (!(obj instanceof Conversion)) {
250: return false;
251: }
252:
253: Conversion that = (Conversion) obj;
254:
255: return this .type.equals(that.type)
256: && this .inboundVariable
257: .equals(that.inboundVariable);
258: }
259:
260: /* (non-Javadoc)
261: * @see java.lang.Object#hashCode()
262: */
263: @Override
264: public int hashCode() {
265: return inboundVariable.hashCode() + type.hashCode();
266: }
267:
268: /* (non-Javadoc)
269: * @see java.lang.Object#toString()
270: */
271: @Override
272: public String toString() {
273: return "Conversion[" + inboundVariable + ","
274: + type.getName() + "]";
275: }
276:
277: private final InboundVariable inboundVariable;
278:
279: private final Class<?> type;
280: }
281:
282: /* (non-Javadoc)
283: * @see java.lang.Object#toString()
284: */
285: @Override
286: public String toString() {
287: StringBuffer buffer = new StringBuffer();
288: buffer.append("InboundContext[");
289: for (Map.Entry<String, InboundVariable> entry : variables
290: .entrySet()) {
291: buffer.append(entry.getKey());
292: buffer.append('=');
293: buffer.append(entry.getValue());
294: buffer.append(',');
295: }
296: buffer.append("]");
297: return buffer.toString();
298: }
299:
300: /**
301: * The stack of pushed conversion contexts.
302: * i.e. What is the context of this type conversion.
303: */
304: private LinkedList<TypeHintContext> typeHintStack = new LinkedList<TypeHintContext>();
305:
306: /**
307: * How many params are there?.
308: * To be more accurate, return one less than the highest numbered parameter
309: * that we have come across.
310: */
311: private int paramCount = 0;
312:
313: /**
314: * A map of all the inbound variables
315: */
316: private final Map<String, InboundVariable> variables = new HashMap<String, InboundVariable>();
317:
318: /**
319: * A map of all the variables converted.
320: */
321: private Map<Conversion, Object> converted = new HashMap<Conversion, Object>();
322:
323: /**
324: * The log stream
325: */
326: private static final Log log = LogFactory
327: .getLog(InboundContext.class);
328: }
|