001: /*
002: * $Id: ComponentDefinition.java 471754 2006-11-06 14:55:09Z husted $
003: *
004: * Licensed to the Apache Software Foundation (ASF) under one
005: * or more contributor license agreements. See the NOTICE file
006: * distributed with this work for additional information
007: * regarding copyright ownership. The ASF licenses this file
008: * to you under the Apache License, Version 2.0 (the
009: * "License"); you may not use this file except in compliance
010: * with the License. You may obtain a copy of the License at
011: *
012: * http://www.apache.org/licenses/LICENSE-2.0
013: *
014: * Unless required by applicable law or agreed to in writing,
015: * software distributed under the License is distributed on an
016: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017: * KIND, either express or implied. See the License for the
018: * specific language governing permissions and limitations
019: * under the License.
020: */
021:
022: package org.apache.struts.tiles;
023:
024: import java.io.Serializable;
025: import java.util.HashMap;
026: import java.util.Map;
027:
028: import org.apache.commons.logging.Log;
029: import org.apache.commons.logging.LogFactory;
030: import org.apache.struts.tiles.xmlDefinition.XmlDefinition;
031: import org.apache.struts.util.RequestUtils;
032:
033: /**
034: * Definition of a template / component attributes.
035: * Attributes of a component can be defined with the help of this class.
036: * An instance of this class can be used as a bean, and passed to 'insert' tag.
037: */
038: public class ComponentDefinition implements Serializable {
039:
040: /**
041: * Commons Logging instance.
042: */
043: protected static Log log = LogFactory
044: .getLog(ComponentDefinition.class);
045:
046: /**
047: * Definition name
048: */
049: protected String name = null;
050:
051: /**
052: * Component / template path (URL).
053: */
054: protected String path = null;
055:
056: /**
057: * Attributes defined for the component.
058: */
059: protected Map attributes = null;
060:
061: /**
062: * Role associated to definition.
063: */
064: protected String role = null;
065:
066: /** Associated Controller URL or classname, if defined */
067: protected String controller = null;
068:
069: /**
070: * Associated Controller typename, if controllerName defined.
071: * Can be CONTROLLER, ACTION or URL, or null.
072: */
073: protected String controllerType = null;
074:
075: /**
076: * Controller name type.
077: */
078: public static final String URL = "url";
079:
080: /**
081: * Controller name type.
082: */
083: public static final String CONTROLLER = "controller";
084:
085: /**
086: * Controller name type.
087: */
088: public static final String ACTION = "action";
089:
090: /**
091: * Controller associated to Definition.
092: * Lazy creation : only on first request
093: */
094: private Controller controllerInstance = null;
095:
096: /**
097: * Constructor.
098: */
099: public ComponentDefinition() {
100: attributes = new HashMap();
101: }
102:
103: /**
104: * Copy Constructor.
105: * Create a new definition initialized with parent definition.
106: * Do a shallow copy : attributes are shared between copies, but not the Map
107: * containing attributes.
108: */
109: public ComponentDefinition(ComponentDefinition definition) {
110: attributes = new HashMap(definition.getAttributes());
111: this .name = definition.getName();
112: this .path = definition.getPath();
113: this .role = definition.getRole();
114: this .controllerInstance = definition.getControllerInstance();
115: this .controller = definition.getController();
116: this .controllerType = definition.getControllerType();
117: }
118:
119: /**
120: * Constructor.
121: * Create a new definition initialized from a RawDefinition.
122: * Raw definitions are used to read definition from a data source (xml file, db, ...).
123: * A RawDefinition mainly contains properties of type String, while Definition
124: * contains more complex type (ex : Controller).
125: * Do a shallow copy : attributes are shared between objects, but not the Map
126: * containing attributes.
127: * OO Design issues : Actually RawDefinition (XmlDefinition) extends ComponentDefinition.
128: * This must not be the case. I have do it because I am lazy.
129: * @throws InstantiationException if an error occur while instanciating Controller :
130: * (classname can't be instanciated, Illegal access with instanciated class,
131: * Error while instanciating class, classname can't be instanciated.
132: */
133: public ComponentDefinition(XmlDefinition definition) {
134:
135: this ((ComponentDefinition) definition);
136: }
137:
138: /**
139: * Constructor.
140: */
141: public ComponentDefinition(String name, String path, Map attributes) {
142: this .name = name;
143: this .path = path;
144: this .attributes = attributes;
145: }
146:
147: /**
148: * Access method for the name property.
149: *
150: * @return the current value of the name property
151: */
152: public String getName() {
153: return name;
154: }
155:
156: /**
157: * Sets the value of the name property.
158: *
159: * @param aName the new value of the name property
160: */
161: public void setName(String aName) {
162: name = aName;
163: }
164:
165: /**
166: * Access method for the path property.
167: *
168: * @return The current value of the path property.
169: */
170: public String getPage() {
171: return path;
172: }
173:
174: /**
175: * Sets the value of the path property.
176: *
177: * @param page the new value of the path property
178: */
179: public void setPage(String page) {
180: path = page;
181: }
182:
183: /**
184: * Access method for the path property.
185: *
186: * @return the current value of the path property
187: */
188: public String getPath() {
189: return path;
190: }
191:
192: /**
193: * Sets the value of the path property.
194: *
195: * @param aPath the new value of the path property
196: */
197: public void setPath(String aPath) {
198: path = aPath;
199: }
200:
201: /**
202: * Access method for the template property.
203: * Same as getPath()
204: * @return the current value of the template property
205: */
206: public String getTemplate() {
207: return path;
208: }
209:
210: /**
211: * Sets the value of the template property.
212: * Same as setPath()
213: *
214: * @param template the new value of the path property
215: */
216: public void setTemplate(String template) {
217: path = template;
218: }
219:
220: /**
221: * Access method for the role property.
222: * @return the current value of the role property
223: */
224: public String getRole() {
225: return role;
226: }
227:
228: /**
229: * Sets the value of the role property.
230: *
231: * @param role the new value of the path property
232: */
233: public void setRole(String role) {
234: this .role = role;
235: }
236:
237: /**
238: * Access method for the attributes property.
239: * If there is no attributes, return an empty map.
240: * @return the current value of the attributes property
241: */
242: public Map getAttributes() {
243: return attributes;
244: }
245:
246: /**
247: * Returns the value of the named attribute as an Object, or null if no
248: * attribute of the given name exists.
249: *
250: * @return requested attribute or null if not found
251: */
252: public Object getAttribute(String key) {
253: return attributes.get(key);
254: }
255:
256: /**
257: * Put a new attribute in this component
258: *
259: * @param key String key for attribute
260: * @param value Attibute value.
261: */
262: public void putAttribute(String key, Object value) {
263: attributes.put(key, value);
264: }
265:
266: /**
267: * Put an attribute in component / template definition.
268: * Attribute can be used as content for tag get.
269: * @param name Attribute name
270: * @param content Attribute value
271: */
272: public void put(String name, Object content) {
273: put(name, content, false, null);
274: }
275:
276: /**
277: * Put an attribute in template definition.
278: * Attribute can be used as content for tag get.
279: * @param name Attribute name
280: * @param content Attribute value �
281: * @param direct Determines how content is handled by get tag: true means content is printed directly; false, the default, means content is included
282: */
283: public void put(String name, Object content, boolean direct) {
284: put(name, content, direct, null);
285: }
286:
287: /**
288: * Put an attribute in template definition.
289: * Attribute can be used as content for tag get.
290: * @param name Attribute name
291: * @param content Attribute value
292: * @param direct Determines how content is handled by get tag: true means content is printed directly; false, the default, means content is included
293: * @param role Determine if content is used by get tag. If user is in role, content is used.
294: */
295: public void put(String name, Object content, boolean direct,
296: String role) {
297: if (direct == true) { // direct String
298: put(name, content, "string", role);
299: } else {
300: put(name, content, "template", role);
301: }
302:
303: }
304:
305: /**
306: * Put an attribute in template definition.
307: * Attribute can be used as content for tag get.
308: * @param name Attribute name
309: * @param content Attribute value
310: * @param type attribute type: template, string, definition
311: * @param role Determine if content is used by get tag. If user is in role, content is used.
312: */
313: public void put(String name, Object content, String type,
314: String role) {
315: // Is there a type set ?
316: // First check direct attribute, and translate it to a valueType.
317: // Then, evaluate valueType, and create requested typed attribute.
318: AttributeDefinition attribute = null;
319:
320: if (content != null && type != null
321: && !(content instanceof AttributeDefinition)) {
322:
323: String strValue = content.toString();
324: if (type.equalsIgnoreCase("string")) {
325: attribute = new DirectStringAttribute(strValue);
326:
327: } else if (type.equalsIgnoreCase("page")) {
328: attribute = new PathAttribute(strValue);
329:
330: } else if (type.equalsIgnoreCase("template")) {
331: attribute = new PathAttribute(strValue);
332:
333: } else if (type.equalsIgnoreCase("instance")) {
334: attribute = new DefinitionNameAttribute(strValue);
335:
336: } else if (type.equalsIgnoreCase("definition")) {
337: attribute = new DefinitionNameAttribute(strValue);
338: }
339: }
340:
341: putAttribute(name, attribute);
342: }
343:
344: /**
345: * Returns a description of the attributes.
346: */
347: public String toString() {
348: return "{name=" + name + ", path=" + path + ", role=" + role
349: + ", controller=" + controller + ", controllerType="
350: + controllerType + ", controllerInstance="
351: + controllerInstance + ", attributes=" + attributes
352: + "}\n";
353: }
354:
355: /**
356: * Get associated controller type.
357: * Type denote a fully qualified classname.
358: */
359: public String getControllerType() {
360: return controllerType;
361: }
362:
363: /**
364: * Set associated controller type.
365: * Type denote a fully qualified classname.
366: * @param controllerType Typeof associated controller
367: */
368: public void setControllerType(String controllerType) {
369: this .controllerType = controllerType;
370: }
371:
372: /**
373: * Set associated controller name as an url, and controller
374: * type as "url".
375: * Name must be an url (not checked).
376: * Convenience method.
377: * @param controller Controller url
378: */
379: public void setControllerUrl(String controller) {
380: setController(controller);
381: setControllerType("url");
382: }
383:
384: /**
385: * Set associated controller name as a classtype, and controller
386: * type as "classname".
387: * Name denote a fully qualified classname
388: * Convenience method.
389: * @param controller Controller classname.
390: */
391: public void setControllerClass(String controller) {
392: setController(controller);
393: setControllerType("classname");
394: }
395:
396: /**
397: * Get associated controller local URL.
398: * URL should be local to webcontainer in order to allow request context followup.
399: * URL comes as a string.
400: */
401: public String getController() {
402: return controller;
403: }
404:
405: /**
406: * Set associated controller URL.
407: * URL should be local to webcontainer in order to allow request context followup.
408: * URL is specified as a string.
409: * @param url Url called locally
410: */
411: public void setController(String url) {
412: this .controller = url;
413: }
414:
415: /**
416: * Get controller instance.
417: * @return controller instance.
418: */
419: public Controller getControllerInstance() {
420: return controllerInstance;
421: }
422:
423: /**
424: * Get or create controller.
425: * Get controller, create it if necessary.
426: * @return controller if controller or controllerType is set, null otherwise.
427: * @throws InstantiationException if an error occur while instanciating Controller :
428: * (classname can't be instanciated, Illegal access with instanciated class,
429: * Error while instanciating class, classname can't be instanciated.
430: */
431: public Controller getOrCreateController()
432: throws InstantiationException {
433:
434: if (controllerInstance != null) {
435: return controllerInstance;
436: }
437:
438: // Do we define a controller ?
439: if (controller == null && controllerType == null) {
440: return null;
441: }
442:
443: // check parameters
444: if (controllerType != null && controller == null) {
445: throw new InstantiationException(
446: "Controller name should be defined if controllerType is set");
447: }
448:
449: controllerInstance = createController(controller,
450: controllerType);
451:
452: return controllerInstance;
453: }
454:
455: /**
456: * Set controller.
457: */
458: public void setControllerInstance(Controller controller) {
459: this .controllerInstance = controller;
460: }
461:
462: /**
463: * Create a new instance of controller named in parameter.
464: * If controllerType is specified, create controller accordingly.
465: * Otherwise, if name denote a classname, create an instance of it. If class is
466: * subclass of org.apache.struts.action.Action, wrap controller
467: * appropriately.
468: * Otherwise, consider name as an url.
469: * @param name Controller name (classname, url, ...)
470: * @param controllerType Expected Controller type
471: * @return org.apache.struts.tiles.Controller
472: * @throws InstantiationException if an error occur while instanciating Controller :
473: * (classname can't be instanciated, Illegal access with instanciated class,
474: * Error while instanciating class, classname can't be instanciated.
475: */
476: public static Controller createController(String name,
477: String controllerType) throws InstantiationException {
478:
479: if (log.isDebugEnabled()) {
480: log.debug("Create controller name=" + name + ", type="
481: + controllerType);
482: }
483:
484: Controller controller = null;
485:
486: if (controllerType == null) { // first try as a classname
487: try {
488: return createControllerFromClassname(name);
489:
490: } catch (InstantiationException ex) { // ok, try something else
491: controller = new UrlController(name);
492: }
493:
494: } else if ("url".equalsIgnoreCase(controllerType)) {
495: controller = new UrlController(name);
496:
497: } else if ("classname".equalsIgnoreCase(controllerType)) {
498: controller = createControllerFromClassname(name);
499: }
500:
501: return controller;
502: }
503:
504: /**
505: * Create a controller from specified classname
506: * @param classname Controller classname.
507: * @return org.apache.struts.tiles.Controller
508: * @throws InstantiationException if an error occur while instanciating Controller :
509: * (classname can't be instanciated, Illegal access with instanciated class,
510: * Error while instanciating class, classname can't be instanciated.
511: */
512: public static Controller createControllerFromClassname(
513: String classname) throws InstantiationException {
514:
515: try {
516: Class requestedClass = RequestUtils
517: .applicationClass(classname);
518: Object instance = requestedClass.newInstance();
519:
520: if (log.isDebugEnabled()) {
521: log.debug("Controller created : " + instance);
522: }
523: return (Controller) instance;
524:
525: } catch (java.lang.ClassNotFoundException ex) {
526: throw new InstantiationException(
527: "Error - Class not found :" + ex.getMessage());
528:
529: } catch (java.lang.IllegalAccessException ex) {
530: throw new InstantiationException(
531: "Error - Illegal class access :" + ex.getMessage());
532:
533: } catch (java.lang.InstantiationException ex) {
534: throw ex;
535:
536: } catch (java.lang.ClassCastException ex) {
537: throw new InstantiationException(
538: "Controller of class '"
539: + classname
540: + "' should implements 'Controller' or extends 'Action'");
541: }
542: }
543:
544: }
|