001: /*
002:
003: This software is OSI Certified Open Source Software.
004: OSI Certified is a certification mark of the Open Source Initiative.
005:
006: The license (Mozilla version 1.0) can be read at the MMBase site.
007: See http://www.MMBase.org/license
008:
009: */
010:
011: package org.mmbase.util.functions;
012:
013: import org.mmbase.core.AbstractDescriptor;
014: import org.mmbase.datatypes.*;
015: import org.mmbase.util.*;
016: import org.mmbase.util.logging.*;
017: import java.util.*;
018: import java.io.*;
019: import org.w3c.dom.*;
020:
021: /**
022: * Each (function) argument is specified by a Parameter object.
023: * A Parameter contains a name and type (it does <em>not</em> contain a value). An array of this is returned by
024: * {@link Function#getParameterDefinition}, and this same array is used to create new empty {@link Parameters}
025: * object (by {@link Function#createParameters}), which can contain actual values for each Parameter.
026: *
027: * @author Daniel Ockeloen (MMFunctionParam)
028: * @author Michiel Meeuwissen
029: * @since MMBase-1.7
030: * @version $Id: Parameter.java,v 1.46 2007/11/25 18:25:49 nklasens Exp $
031: * @see Parameters
032: */
033:
034: public class Parameter<C> extends AbstractDescriptor implements
035: java.io.Serializable {
036: private static final Logger log = Logging
037: .getLoggerInstance(Parameter.class);
038:
039: private static final long serialVersionUID = 1L;
040: /**
041: * Parameter which might be needed in lots of Parameter definitions. These parameters are
042: * 'standard' parameters, which can be filled in by the system. E.g. the mmbase taglib uses
043: * these constants, and if it has a cloud ('mm:cloud is used'), then cloud-parameters are filled
044: * automaticly.
045: */
046: public static final Parameter<String> LANGUAGE = new Parameter<String>(
047: "language", String.class);
048: public static final Parameter<Locale> LOCALE = new Parameter<Locale>(
049: "locale", Locale.class);
050: public static final Parameter<org.mmbase.security.UserContext> USER = new Parameter<org.mmbase.security.UserContext>(
051: "user", org.mmbase.security.UserContext.class);
052: public static final Parameter<javax.servlet.http.HttpServletResponse> RESPONSE = new Parameter<javax.servlet.http.HttpServletResponse>(
053: "response", javax.servlet.http.HttpServletResponse.class);
054: public static final Parameter<javax.servlet.http.HttpServletRequest> REQUEST = new Parameter<javax.servlet.http.HttpServletRequest>(
055: "request", javax.servlet.http.HttpServletRequest.class);
056: public static final Parameter<org.mmbase.bridge.Cloud> CLOUD = new Parameter<org.mmbase.bridge.Cloud>(
057: "cloud", org.mmbase.bridge.Cloud.class);
058:
059: /**
060: * 'system' parameter set for nodefunctions.
061: * @since MMBase-1.8
062: */
063: public static final Parameter<org.mmbase.bridge.Node> NODE = new Parameter<org.mmbase.bridge.Node>(
064: "_node", org.mmbase.bridge.Node.class);
065: public final static Parameter CORENODE = new Parameter("_corenode",
066: Object.class); // object because otherwise problems with RMMCI which doesn't have MMObjectNode.
067:
068: public static final Parameter<String> FIELD = new Parameter<String>(
069: "field", String.class);
070:
071: /**
072: * An empty Parameter array.
073: */
074: @SuppressWarnings("unchecked")
075: public static final Parameter[] EMPTY = new Parameter[0];
076:
077: @SuppressWarnings({"unchecked","cast"})
078: public static final <C> Parameter<C>[] emptyArray() {
079: return (Parameter<C>[]) EMPTY;
080: }
081:
082: /**
083: * @since MMBase-1.9
084: */
085: public static Parameter<?>[] readArrayFromXml(Element element) {
086: List<Parameter<?>> list = new ArrayList<Parameter<?>>();
087: org.w3c.dom.NodeList params = element.getChildNodes();
088: for (int i = 0; i < params.getLength(); i++) {
089: Node n = params.item(i);
090: if (n instanceof Element && "param".equals(n.getNodeName())) {
091: Parameter<?> parameter = readFromXml((Element) n);
092: list.add(parameter);
093: }
094: }
095: return list.toArray(Parameter.emptyArray());
096: }
097:
098: /**
099: * @since MMBase-1.9
100: */
101: public static <C> Parameter<C> readFromXml(Element element) {
102: String name = element.getAttribute("name");
103: String type = element.getAttribute("type");
104: String required = element.getAttribute("required");
105: String description = element.getAttribute("description"); // actually description as attribute is not very sane
106: Parameter<C> parameter = new Parameter<C>(name,
107: (Class<C>) getClassForName(type));
108: if (!"".equals(description)) {
109: parameter.getLocalizedDescription().set(description, null); // just set it for the default locale...
110: }
111: parameter.dataType.setRequired("true".equals(required));
112:
113: // check for a default value
114: if (element.getFirstChild() != null) {
115: parameter.setDefaultValue(parameter
116: .autoCast(org.mmbase.util.xml.DocumentReader
117: .getNodeTextValue(element)));
118: }
119: return parameter;
120: }
121:
122: /**
123: * @since MMBase-1.9
124: */
125: public static Class<?> getClassForName(String type) {
126: Class<?> clazz;
127: try {
128: boolean fullyQualified = type.indexOf('.') > -1;
129: if (!fullyQualified) {
130: if (type.equals("int")) { // needed?
131: clazz = int.class;
132: } else if (type.equals("NodeList")) {
133: clazz = org.mmbase.bridge.NodeList.class;
134: } else if (type.equals("Node")) {
135: clazz = org.mmbase.bridge.Node.class;
136: } else {
137: clazz = Class.forName("java.lang." + type);
138: }
139: } else {
140: clazz = Class.forName(type);
141: }
142: } catch (ClassNotFoundException cne) {
143: log.warn("Cannot determine parameter type : '" + type
144: + "', using Object as type instead.");
145: clazz = Object.class;
146: }
147: return clazz;
148: }
149:
150: // implementation of serializable, I hate java. Cannot make AbstractDescriptor Serializable, so doing it here.... sigh sigh.
151: // If you would make AbstractDescriptor Serializable, CoreField will become Serializable and MMObjectBuilder needs to be serializable then (because it is a member of CoreField).
152: private void writeObject(ObjectOutputStream out) throws IOException {
153: out.writeUTF(key);
154: out.writeObject(description);
155: out.writeObject(guiName);
156: out.writeObject(dataType);
157: }
158:
159: // implementation of serializable
160: private void readObject(ObjectInputStream in) throws IOException,
161: ClassNotFoundException {
162: key = in.readUTF();
163: description = (LocalizedString) in.readObject();
164: guiName = (LocalizedString) in.readObject();
165: dataType = (DataType<C>) in.readObject();
166: }
167:
168: /**
169: * The parameter's data type
170: * @since MMBase-1.8
171: */
172: protected DataType<C> dataType;
173:
174: /**
175: * Create a Parameter object
176: * @param name the name of the parameter
177: * @param dataType the datatype of the parameter to copy
178: * @since MMBase-1.8
179: */
180: public Parameter(String name, DataType<C> dataType) {
181: this (name, dataType, true);
182: }
183:
184: /**
185: * Create a Parameter object
186: * @param name the name of the parameter
187: * @param dataType the datatype of the parameter to assign or copy
188: * @param copy if <code>true</code>, teh datatype is copied. if not, it is assigned directly,
189: * that is, changing condfiitons on the parameter changes the passed datatype instance.
190: * @since MMBase-1.8
191: */
192: public Parameter(String name, DataType<C> dataType, boolean copy) {
193: super (name);
194: if (copy) {
195: this .dataType = dataType.clone(name);
196: } else {
197: this .dataType = dataType;
198: }
199: }
200:
201: /**
202: * Create a Parameter object
203: * @param name the name of the parameter
204: * @param type the class of the parameter's possible value
205: */
206: public Parameter(String name, Class<C> type) {
207: super (name);
208: dataType = DataTypes.createDataType(name, type);
209: }
210:
211: /**
212: * Create a Parameter object
213: * @param name the name of the parameter
214: * @param type the class of the parameter's possible value
215: * @param required whether the parameter requires a value
216: */
217: public Parameter(String name, Class<C> type, boolean required) {
218: this (name, type);
219: dataType.setRequired(required);
220: }
221:
222: /**
223: * Create a Parameter object
224: * @param name the name of the parameter
225: * @param type the class of the parameter's possible value
226: * @param defaultValue the value to use if the parameter has no value set
227: */
228: public Parameter(String name, Class<C> type, C defaultValue) {
229: this (name, type);
230: dataType.setDefaultValue(defaultValue);
231: }
232:
233: @SuppressWarnings("unchecked")
234: public Parameter(String name, C defaultValue) {
235: this (name, (Class<C>) defaultValue.getClass());
236: dataType.setDefaultValue(defaultValue);
237: }
238:
239: @SuppressWarnings("unchecked")
240: protected static <C> Class<C> getClass(C v) {
241: return (Class<C>) (v == null ? Object.class : v.getClass());
242: }
243:
244: /**
245: * Create Parameter definition by example value
246: * @since MMBase-1.9
247: */
248: public Parameter(Map.Entry<String, C> entry) {
249: this (entry.getKey(), getClass(entry.getValue()));
250: }
251:
252: /**
253: * Copy-constructor, just to copy it with different requiredness
254: */
255: public Parameter(Parameter<C> p, boolean required) {
256: this (p.key, p.getDataType());
257: dataType.setRequired(required);
258: }
259:
260: /**
261: * Copy-constructor, just to copy it with different defaultValue (which implies that it is not required now)
262: */
263: public Parameter(Parameter<C> p, C defaultValue) {
264: this (p.key, p.getDataType());
265: dataType.setDefaultValue(defaultValue);
266: }
267:
268: /**
269: * Returns the default value of this parameter (derived from the datatype).
270: * @return the default value
271: */
272: public C getDefaultValue() {
273: return dataType.getDefaultValue();
274: }
275:
276: /**
277: * Sets the default value of this parameter.
278: * @param defaultValue the default value
279: */
280: public void setDefaultValue(C defaultValue) {
281: dataType.setDefaultValue(defaultValue);
282: }
283:
284: /**
285: * Returns the data type of this parameter.
286: * @return the datatype
287: * @since MMBase-1.8
288: */
289: public DataType<C> getDataType() {
290: return dataType;
291: }
292:
293: /**
294: * Returns the type of values that this parameter accepts.
295: * @return the type as a Class
296: */
297: public Class<C> getTypeAsClass() {
298: return dataType.getTypeAsClass();
299: }
300:
301: /**
302: * Returns whether the parameter requires a value.
303: * @return <code>true</code> if a value is required
304: */
305: public boolean isRequired() {
306: return dataType.isRequired();
307: }
308:
309: /**
310: * Checks if the passed object is of the correct class (compatible with the type of this Parameter),
311: * and throws an IllegalArgumentException if it doesn't.
312: * @param value teh value whose type (class) to check
313: * @throws IllegalArgumentException if the type is not compatible
314: */
315: public void checkType(Object value) {
316: dataType.checkType(value);
317: }
318:
319: /**
320: * Tries to 'cast' an object for use with this parameter. E.g. if value is a String, but this
321: * parameter is of type Integer, then the string can be parsed to Integer.
322: * @param value The value to be filled in in this Parameter.
323: */
324: protected C autoCast(Object value) {
325: return dataType.cast(value, null, null);
326: }
327:
328: public int hashCode() {
329: return getName().hashCode() + 13 * getDataType().hashCode();
330: }
331:
332: /**
333: * Whether parameter equals to other parameter. Only key and type are consided. DefaultValue and
334: * required propererties are only 'utilities'.
335: * @return true if o is Parameter of which key and type equal to this' key and type.
336: */
337: public boolean equals(Object o) {
338: if (o instanceof Parameter) {
339: Parameter<?> a = (Parameter<?>) o;
340: return a.getName().equals(getName())
341: && a.getDataType().equals(getDataType());
342: }
343: return false;
344: }
345:
346: public String toString() {
347: return getTypeAsClass().getName() + " " + getName();
348: }
349:
350: /**
351: * A Parameter.Wrapper wraps one Parameter around a Parameter[] (then you can put it in a
352: * Parameter[]). Parameters will recognize this. This can be used when you 'extend'
353: * functionality, and add more parameters. The Parameter array can contain such a
354: * Parameter.Wrapper object containing the original Parameter array.
355: */
356: public static class Wrapper extends Parameter {
357: Parameter[] arguments;
358:
359: public Wrapper(Parameter... arg) {
360: super ("[ARRAYWRAPPER]", Parameter[].class);
361: arguments = arg;
362: }
363:
364: /**
365: * @since MMBase-1.9
366: */
367: public Parameter[] getArguments() {
368: return arguments;
369: }
370:
371: // this toString makes the wrapping invisible in the toString of a wrapping Parameter[]
372: public String toString() {
373: StringBuilder buf = new StringBuilder();
374: for (int i = 0; i < arguments.length; i++) {
375: if (i > 0)
376: buf.append(", ");
377: buf.append(arguments[i].toString());
378:
379: }
380: return buf.toString();
381:
382: }
383: }
384:
385: }
|