001 /*
002 * Parameter.java
003 *
004 * Copyright (c) 1995-2010, The University of Sheffield. See the file
005 * COPYRIGHT.txt in the software or at http://gate.ac.uk/gate/COPYRIGHT.txt
006 *
007 * This file is part of GATE (see http://gate.ac.uk/), and is free
008 * software, licenced under the GNU Library General Public License,
009 * Version 2, June 1991 (in the distribution as file licence.html,
010 * and also available at http://gate.ac.uk/gate/licence.html).
011 *
012 * Hamish Cunningham, 15/Oct/2000
013 *
014 * $Id: Parameter.java 12006 2009-12-01 17:24:28Z thomas_heitz $
015 */
016
017 package gate.creole;
018
019 import java.io.Serializable;
020 import java.net.MalformedURLException;
021 import java.net.URL;
022 import java.util.*;
023
024 import gate.Factory;
025 import gate.FeatureMap;
026 import gate.Gate;
027 import gate.util.*;
028
029
030 /** Models a resource parameter.
031 */
032 public class Parameter implements Serializable
033 {
034 /**
035 * Constructor
036 * @param baseUrl the URL to the creole.xml file that defines the resource
037 * this parameter belongs to. This will be used a context when deriving
038 * default values for the parameters of type URL.
039 */
040 public Parameter(URL baseUrl){
041 this.baseURL = baseUrl;
042 }
043
044 /** The type name of the parameter */
045 String typeName;
046
047 /** Set the type name for this parameter */
048 public void setTypeName(String typeName) { this.typeName = typeName; }
049
050 /** Get the type name for this parameter */
051 public String getTypeName() { return typeName; }
052
053 /** Is the parameter optional? */
054 boolean optional = false;
055
056 /** Set optionality of this parameter */
057 public void setOptional(boolean optional) { this.optional = optional; }
058
059 /** Is the parameter optional? */
060 public boolean isOptional() { return optional; }
061
062 /** The name of the item's class. If the parameter is a collection then
063 * we need to know the class of its items in order to create them the
064 * way we want.
065 */
066 String itemClassName = null;
067
068 /** A set of strings representing suffixes for URL params*/
069 Set suffixes = null;
070
071 /**
072 * Map giving concrete classes that should be used for a parameter
073 * whose declared type is an interface. This substitution allows a
074 * resource to specify a parameter of type, e.g. java.util.List,
075 * and give it a default value in creole.xml. The runtime class of
076 * the default value will be taken from this map.
077 *
078 * Note that for this to work, it must be the case that for every
079 * key <i>k</i> in this map,
080 *
081 * <code>k.isAssignableFrom(substituteClasses.get(k))</code>
082 */
083 static Map<Class, Class> substituteClasses =
084 new HashMap<Class, Class>();
085
086 static {
087 substituteClasses.put(Collection.class, ArrayList.class);
088 substituteClasses.put(List.class, ArrayList.class);
089 substituteClasses.put(Set.class, HashSet.class);
090 substituteClasses.put(SortedSet.class, TreeSet.class);
091 substituteClasses.put(Queue.class, LinkedList.class);
092 }
093
094 /** Calculate and return the default value for this parameter */
095 public Object calculateDefaultValue() throws ParameterException {
096 // if there's no default string and this is a builtin type, return null
097 if(
098 defaultValueString == null && typeName != null &&
099 typeName.startsWith("java.")
100 )
101 return null;
102
103 return calculateValueFromString(defaultValueString);
104 } // calculateDefaultValue()
105
106 /** Calculate and return the value for this parameter starting from a String
107 */
108 public Object calculateValueFromString(String stringValue)
109 throws ParameterException {
110 //if we have no string we can't construct a value
111 Object value = null;
112
113 // get the Class for the parameter via Class.forName or CREOLE register
114 Class paramClass = getParameterClass();
115 if(substituteClasses.containsKey(paramClass)) {
116 paramClass = substituteClasses.get(paramClass);
117 }
118
119
120 // Test if the paramClass is a collection and if it is, try to
121 // construct the param as a collection of items specified in the
122 // default string value. If paramClass is an interface type we
123 // look up its substitute concrete type in the map
124 // collectionSubstituteClasses and create a value of that type.
125 if (Collection.class.isAssignableFrom(paramClass) &&
126 !paramClass.isInterface()){
127 // Create an collection object belonging to paramClass
128 Collection colection = null;
129 try{
130 colection = (Collection)paramClass.getConstructor(new Class[]{}).
131 newInstance(new Object[]{});
132 } catch(Exception ex){
133 throw new ParameterException("Could not construct an object of type "
134 + typeName + " for param " + name +
135 "\nProblem was: " + ex.toString());
136 }// End try
137 // If an itemClassName was specified then try to create objects belonging
138 // to this class and add them to the collection. Otherwise add the
139 // string tokens to the collection.
140 if(itemClassName == null){
141 // Read the tokens from the default value and try to create items
142 // belonging to the itemClassName
143 StringTokenizer strTokenizer = new StringTokenizer(
144 stringValue,";");
145 while(strTokenizer.hasMoreTokens()){
146 String itemStringValue = strTokenizer.nextToken();
147 colection.add(itemStringValue);
148 }// End while
149 }else{
150 Class itemClass = null;
151 try{
152 itemClass = Gate.getClassLoader().loadClass(itemClassName);
153 }catch(ClassNotFoundException e){
154 throw new ParameterException("Could not construct a class object for "
155 + itemClassName + " for param "+ name +
156 ", with type name="+ typeName);
157 }// End try
158 // Read the tokens from the default value and try to create items
159 // belonging to the itemClassName
160 StringTokenizer strTokenizer = new StringTokenizer(
161 stringValue,";");
162 while(strTokenizer.hasMoreTokens()){
163 // Read a string item and construct an object belonging to
164 // itemClassName
165 String itemStringValue = strTokenizer.nextToken();
166 Object itemValue = null;
167 try{
168 itemValue = itemClass.getConstructor(new Class[]{String.class}).
169 newInstance(new Object[]{itemStringValue});
170 }catch(Exception e){
171 throw new ParameterException("Could not create an object of " +
172 itemClassName + " for param name "+ name + ", with type name ="+
173 typeName);
174 }// End try
175 // Add the item value object to the collection
176 colection.add(itemValue);
177 }// End while
178 }// End if(itemClassName == null)
179 return colection;
180 }// End if (Collection.class.isAssignableFrom(paramClass))
181
182 if(FeatureMap.class.isAssignableFrom(paramClass)) {
183 // a null string value means a null FeatureMap
184 if(stringValue == null) return null;
185 FeatureMap fm = null;
186 // if the type is an interface type (not a concrete implementation)
187 // then just create a normal feature map using the factory
188 if(paramClass.isInterface()) {
189 fm = Factory.newFeatureMap();
190 }
191 else {
192 try{
193 fm = (FeatureMap)paramClass.getConstructor(new Class[]{}).
194 newInstance(new Object[]{});
195 } catch(Exception ex){
196 throw new ParameterException("Could not construct an object of type "
197 + typeName + " for param " + name +
198 "\nProblem was: " + ex.toString());
199 }
200 }
201
202 // Read the tokens from the default value and try to create items
203 // belonging to the itemClassName
204 StringTokenizer strTokenizer = new StringTokenizer(
205 stringValue,";");
206 while(strTokenizer.hasMoreTokens()) {
207 String keyAndValue = strTokenizer.nextToken();
208 int indexOfEquals = keyAndValue.indexOf('=');
209 if(indexOfEquals == -1) {
210 throw new ParameterException("Error parsing string \""
211 + stringValue + "\" for parameter " + name + " of type "
212 + typeName + ". Value string must be of the form "
213 + "name1=value1;name2=value2;...");
214 }
215 String featName = keyAndValue.substring(0, indexOfEquals);
216 String featValue = keyAndValue.substring(indexOfEquals + 1);
217 fm.put(featName, featValue);
218 }
219
220 return fm;
221 }
222
223 // Java 5.0 enum types
224 if(paramClass.isEnum()) {
225 if(stringValue == null) {
226 value = null;
227 }
228 else {
229 try {
230 value = Enum.valueOf(paramClass, stringValue);
231 }
232 catch(IllegalArgumentException e) {
233 throw new ParameterException("Invalid enum constant name "
234 + stringValue + " for type " + typeName);
235 }
236 }
237 }
238 // java builtin types - for numeric types, we don't attempt to parse an
239 // empty string value, but just leave value as null
240 else if(typeName.startsWith("java.")) {
241 if(typeName.equals("java.lang.Boolean"))
242 value = Boolean.valueOf(stringValue);
243 else if(typeName.equals("java.lang.Long")) {
244 if(stringValue != null && !stringValue.equals("")) {
245 value = Long.valueOf(stringValue);
246 }
247 }
248 else if(typeName.equals("java.lang.Integer")) {
249 if(stringValue != null && !stringValue.equals("")) {
250 value = Integer.valueOf(stringValue);
251 }
252 }
253 else if(typeName.equals("java.lang.String"))
254 value = stringValue;
255 else if(typeName.equals("java.lang.Double")) {
256 if(stringValue != null && !stringValue.equals("")) {
257 value = Double.valueOf(stringValue);
258 }
259 }
260 else if(typeName.equals("java.lang.Float")) {
261 if(stringValue != null && !stringValue.equals("")) {
262 value = Float.valueOf(stringValue);
263 }
264 }
265 else if(typeName.equals("java.net.URL"))
266 try{
267 if(stringValue != null && !stringValue.equals("")) {
268 value = new URL(baseURL, stringValue);
269 }
270 }catch(MalformedURLException mue){
271 value = null;
272 }
273 else{
274 //try to construct a new value from the string using a constructor
275 // e.g. for URLs
276 try{
277 if(!paramClass.isAssignableFrom(String.class)){
278 value = paramClass.getConstructor(new Class[]{String.class}).
279 newInstance(new Object[]{stringValue});
280 }
281 }catch(Exception e){
282 throw new ParameterException("Unsupported parameter type " + typeName);
283 }
284 }
285 } else {
286 // non java types
287 if(resData == null)
288 resData = (ResourceData) Gate.getCreoleRegister().get(typeName);
289 if(resData == null){
290 //unknown type
291 return null;
292 }
293
294 List instantiations = resData.getInstantiations();
295 if(! instantiations.isEmpty()) value = instantiations.get(0);
296 }
297
298 return value;
299 } // calculateValueFromString()
300
301
302 /** The resource data that this parameter is part of. */
303 protected ResourceData resData;
304
305 /** Get the default value for this parameter. If the value is
306 * currently null it will try and calculate a value.
307 * @see #calculateDefaultValue()
308 */
309 public Object getDefaultValue() throws ParameterException {
310 return calculateDefaultValue();
311 } // getDefaultValue
312
313 /** Default value string (unprocessed, from the metadata)
314 * for the parameter
315 */
316 String defaultValueString;
317
318 /** Set the default value string (from the metadata)
319 * for the parameter
320 */
321 public void setDefaultValueString(String defaultValueString) {
322 this.defaultValueString = defaultValueString;
323 } // setDefaultValueString
324
325 /** Get the default value string (unprocessed, from the metadata)
326 * for the parameter
327 */
328 public String getDefaultValueString() { return defaultValueString; }
329
330 /** Comment for the parameter */
331 String comment;
332
333 /** Set the comment for this parameter */
334 public void setComment(String comment) { this.comment = comment; }
335
336 /** Get the comment for this parameter */
337 public String getComment() { return comment; }
338
339 /** helpURL for the parameter */
340 String helpURL;
341
342 /** Set the helpURL for this parameter */
343 public void sethelpURL(String helpURL) { this.helpURL = helpURL; }
344
345 /** Get the helpURL for this parameter */
346 public String gethelpURL() { return helpURL; }
347
348 /** Name for the parameter */
349 String name;
350
351 /** Set the name for this parameter */
352 public void setName(String name) { this.name = name; }
353
354 /** Get the name for this parameter */
355 public String getName() { return name; }
356
357 /** Get the suffixes atached with this param. If it's null then there are
358 * no suffices attached with it
359 */
360 public Set getSuffixes(){ return suffixes;}
361
362 /** Is this a run-time parameter? */
363 boolean runtime = false;
364
365 /**
366 * The URL to the creole.xml file that defines the resource this parameter
367 * belongs to. It is used for deriving default values for parameters of type
368 * {@link URL}.
369 */
370 protected URL baseURL;
371 /** Set runtime status of this parameter */
372 public void setRuntime(boolean runtime) { this.runtime = runtime; }
373
374 /** Is the parameter runtime? */
375 public boolean isRuntime() { return runtime; }
376
377 /** The Class for the parameter type */
378 protected Class paramClass;
379
380 /** Find the class for this parameter type. */
381 protected Class getParameterClass() throws ParameterException
382 {
383 // get java builtin classes via class; else look in the register
384 try {
385 ResourceData resData = (ResourceData)
386 Gate.getCreoleRegister().get(typeName);
387 if(resData == null){
388 paramClass = Gate.getClassLoader().loadClass(typeName);
389 }else{
390 paramClass = resData.getResourceClass();
391 }
392
393 // if(typeName.startsWith("java."))
394 // paramClass = Class.forName(typeName);
395 // else {
396 // ResourceData resData =
397 // (ResourceData) Gate.getCreoleRegister().get(typeName);
398 // if(resData == null)
399 // throw new ParameterException(
400 // "No resource data for " + typeName + " in Parameter/getParamClz"
401 // );
402 // paramClass = resData.getResourceClass();
403 // }
404 } catch(ClassNotFoundException e) {
405 throw new ParameterException(
406 "Couldn't find class " + typeName + ": " + Strings.getNl() + e
407 );
408 }
409
410 if(paramClass == null)
411 throw new ParameterException("Couldn't find class " + typeName);
412
413 return paramClass;
414 } // getParameterClass
415
416 /** String representation */
417 public String toString() {
418 try{
419 return "Parameter: name="+ name+ "; valueString=" + typeName +
420 "; optional=" + optional +
421 "; defaultValueString=" + defaultValueString +
422 "; defaultValue=" + getDefaultValue() + "; comment=" +
423 comment + "; helpURL=" +
424 helpURL + "; runtime=" + runtime +
425 "; itemClassName=" + itemClassName +
426 "; suffixes=" + suffixes;
427 }catch(ParameterException pe){
428 throw new GateRuntimeException(pe.toString());
429 }
430 }
431
432 /**
433 * If this parameter is a List type this will return the type of the items
434 * in the list. If the type is <tt>null</tt> String will be assumed.
435 */
436 public String getItemClassName() {
437 return itemClassName;
438 } // toString()
439 } // class Parameter
|