001: /**********************************************************************************
002: * $URL:https://source.sakaiproject.org/svn/osp/trunk/common/api-impl/src/java/org/theospi/portfolio/shared/model/impl/GenericXmlRenderer.java $
003: * $Id:GenericXmlRenderer.java 9134 2006-05-08 20:28:42Z chmaurer@iupui.edu $
004: ***********************************************************************************
005: *
006: * Copyright (c) 2005, 2006 The Sakai Foundation.
007: *
008: * Licensed under the Educational Community License, Version 1.0 (the "License");
009: * you may not use this file except in compliance with the License.
010: * You may obtain a copy of the License at
011: *
012: * http://www.opensource.org/licenses/ecl1.php
013: *
014: * Unless required by applicable law or agreed to in writing, software
015: * distributed under the License is distributed on an "AS IS" BASIS,
016: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: * See the License for the specific language governing permissions and
018: * limitations under the License.
019: *
020: **********************************************************************************/package org.theospi.portfolio.shared.model.impl;
021:
022: import java.beans.BeanInfo;
023: import java.beans.IntrospectionException;
024: import java.beans.Introspector;
025: import java.beans.PropertyDescriptor;
026: import java.io.InputStream;
027: import java.lang.reflect.InvocationTargetException;
028: import java.lang.reflect.Method;
029: import java.util.Collection;
030: import java.util.Iterator;
031:
032: import org.apache.commons.logging.Log;
033: import org.apache.commons.logging.LogFactory;
034: import org.jdom.Document;
035: import org.jdom.Element;
036: import org.jdom.input.SAXBuilder;
037: import org.sakaiproject.metaobj.shared.ArtifactFinder;
038: import org.sakaiproject.metaobj.shared.mgt.PresentableObjectHome;
039: import org.sakaiproject.metaobj.shared.model.Artifact;
040: import org.sakaiproject.metaobj.shared.model.Id;
041: import org.sakaiproject.metaobj.utils.xml.SchemaInvalidException;
042: import org.theospi.portfolio.shared.model.OspException;
043:
044: /**
045: * This class renders an object into an xml object. This implementation
046: * uses bean introspection to navigate the object model and convert into
047: * a jdom model. In relies on the objectStructure xml file to specify which
048: * properties to traverse. The prevents circlular references from being
049: * traversed.
050: *
051: * Valid values for type attribute are: collection, artifact, object.
052: * If not specified object is assumed.
053: *
054: *
055: */
056: public class GenericXmlRenderer implements PresentableObjectHome {
057: protected final Log logger = LogFactory.getLog(getClass());
058: private ArtifactFinder artifactFinder;
059: private String objectStructure;
060: private String rootName;
061: private String supportedType;
062: private String artifactType;
063:
064: protected Element getObjectStructureRoot() {
065: SAXBuilder builder = new SAXBuilder();
066: try {
067: InputStream is = getClass().getResourceAsStream(
068: getObjectStructure());
069: Document doc = builder.build(is);
070: return doc.getRootElement();
071: } catch (Exception e) {
072: throw new SchemaInvalidException(e);
073: }
074: }
075:
076: protected Element getXml(Object object) {
077: Element rootElement = new Element(getRootName());
078: try {
079: addObjectNodeInfo(rootElement, object,
080: getObjectStructureRoot());
081: } catch (IntrospectionException e) {
082: logger.error("", e);
083: }
084:
085: return rootElement;
086: }
087:
088: protected boolean isTraversableType(PropertyDescriptor descriptor,
089: Element structure) {
090: if (structure == null) {
091: return false;
092: }
093: if (structure.getChild(descriptor.getName()) != null
094: || structure.getName().equals(descriptor.getName())) {
095: return true;
096: }
097:
098: return false;
099: }
100:
101: protected void addObjectNodeInfo(Element parentNode, Object object,
102: Element structure) throws IntrospectionException {
103: if (object == null)
104: return;
105: // go through each property... put each one in...
106: logger.debug("adding object of class " + object.getClass());
107:
108: BeanInfo info = Introspector.getBeanInfo(object.getClass());
109:
110: PropertyDescriptor[] props = info.getPropertyDescriptors();
111:
112: for (int i = 0; i < props.length; i++) {
113: PropertyDescriptor property = props[i];
114: logger.debug("examining property: " + property.getName());
115: if (isTraversableType(property, structure)) {
116: if (isCollection(property, structure)) {
117: addCollectionItems(parentNode, property, object,
118: structure);
119: } else if (isArtifact(property, structure)) {
120: addArtifactItem(parentNode, property, object);
121: } else {
122: addItem(parentNode, property, object, structure);
123: }
124: } else {
125: addItemToXml(parentNode, property, object);
126: }
127:
128: }
129: }
130:
131: protected void addItemToXml(Element parentNode,
132: PropertyDescriptor prop, Object object) {
133: String attribName = prop.getName();
134:
135: logger.debug("adding attribute: " + attribName);
136:
137: Method readMethod = prop.getReadMethod();
138: if (readMethod == null
139: || readMethod.getParameterTypes().length > 0) {
140: logger.debug("skipping attrib: " + attribName);
141: return;
142: }
143:
144: Element attribute = new Element(attribName);
145: Object attribValue = null;
146:
147: try {
148: attribValue = readMethod.invoke(object, (Object[]) null);
149: } catch (IllegalAccessException e) {
150: logger.error("could not get attribute", e);
151: } catch (InvocationTargetException e) {
152: logger.error("could not get attribute", e);
153: }
154: if (attribValue != null && attribValue.toString().length() > 0) {
155: logger.debug("value for attrib " + attribName
156: + " is not null.");
157: attribute.addContent(attribValue.toString());
158: }
159:
160: parentNode.addContent(attribute);
161: }
162:
163: protected void addItem(Element parentNode, PropertyDescriptor prop,
164: Object object, Element structure) {
165: logger.debug("addItem()");
166:
167: Method readMethod = prop.getReadMethod();
168: Element newElement = new Element(prop.getName());
169: try {
170: Object newObject = readMethod.invoke(object,
171: (Object[]) null);
172: parentNode.addContent(newElement);
173: addObjectNodeInfo(newElement, newObject, structure
174: .getChild(prop.getName()));
175: } catch (IllegalAccessException e) {
176: logger.error("could not get attribute", e);
177: } catch (InvocationTargetException e) {
178: logger.error("could not get attribute", e);
179: } catch (IntrospectionException e) {
180: logger.error("could not get attribute", e);
181: }
182: }
183:
184: protected void addArtifactItem(Element parentNode,
185: PropertyDescriptor prop, Object object) {
186: logger.debug("addArtifactItem()");
187:
188: Method readMethod = prop.getReadMethod();
189:
190: Id artifactId = null;
191:
192: try {
193: artifactId = (Id) readMethod
194: .invoke(object, (Object[]) null);
195: } catch (IllegalAccessException e) {
196: logger.error("could not get attribute", e);
197: } catch (InvocationTargetException e) {
198: logger.error("could not get attribute", e);
199: }
200:
201: logger.debug("finding artifact with id=" + artifactId);
202:
203: Artifact art = getArtifactFinder().load(artifactId);
204: if (art.getHome() instanceof PresentableObjectHome) {
205: PresentableObjectHome home = (PresentableObjectHome) art
206: .getHome();
207: Element node = home.getArtifactAsXml(art);
208: node.setName("artifact");
209: parentNode.addContent(node);
210: }
211: }
212:
213: protected void addCollectionItems(Element parentNode,
214: PropertyDescriptor prop, Object object, Element structure) {
215: logger.debug("addCollectionItems()");
216:
217: Method readMethod = prop.getReadMethod();
218: Element newListElement = new Element(prop.getName());
219: try {
220: Object newObject = readMethod.invoke(object,
221: (Object[]) null);
222: parentNode.addContent(newListElement);
223: Collection items = (Collection) newObject;
224: int index = 0;
225: for (Iterator i = items.iterator(); i.hasNext();) {
226: Element newElement = new Element(
227: getCollectionItemName(prop.getName()));
228: newElement.setAttribute("index", String.valueOf(index));
229: newListElement.addContent(newElement);
230:
231: Element elementStructure = structure.getChild(prop
232: .getName());
233: if (elementStructure == null
234: && structure.getAttributeValue("isNested") != null
235: && structure.getAttributeValue("isNested")
236: .equals("true")) {
237: elementStructure = structure.getParentElement()
238: .getChild(prop.getName());
239: }
240: addObjectNodeInfo(newElement, i.next(),
241: elementStructure);
242: index++;
243: }
244: } catch (IllegalAccessException e) {
245: logger.error("could not get attribute", e);
246: } catch (InvocationTargetException e) {
247: logger.error("could not get attribute", e);
248: } catch (IntrospectionException e) {
249: logger.error("could not get attribute", e);
250: }
251:
252: }
253:
254: protected String getCollectionItemName(String listName) {
255: if (listName.endsWith("s")) {
256: return listName.substring(0, listName.length() - 1);
257: }
258: return listName;
259: }
260:
261: protected boolean isArtifact(PropertyDescriptor prop,
262: Element structure) {
263: Element child = structure.getChild(prop.getName());
264: String typeAttribute = child.getAttributeValue("type");
265: if (typeAttribute != null && typeAttribute.equals("artifact")) {
266: return true;
267: }
268: return false;
269: }
270:
271: protected boolean isCollection(PropertyDescriptor prop,
272: Element structure) {
273: Element elementStructure = structure.getChild(prop.getName());
274: if (elementStructure == null
275: && structure.getAttributeValue("isNested") != null
276: && structure.getAttributeValue("isNested").equals(
277: "true")) {
278: elementStructure = structure.getParentElement().getChild(
279: prop.getName());
280: }
281: String typeAttribute = elementStructure
282: .getAttributeValue("type");
283: if (typeAttribute != null && typeAttribute.equals("collection")) {
284: return true;
285: }
286: return false;
287: }
288:
289: public Element getArtifactAsXml(Artifact artifact) {
290: try {
291: Class supportedType = Class.forName(getSupportedType());
292: if (supportedType.isAssignableFrom(artifact.getClass())) {
293: return getXml(artifact);
294: }
295: } catch (ClassNotFoundException e) {
296: throw new RuntimeException(getSupportedType()
297: + " is not a valid class: " + e.getMessage(), e);
298: }
299:
300: throw new OspException("Expecting object of type: "
301: + getSupportedType() + " but found object of type "
302: + artifact.getClass());
303: }
304:
305: public ArtifactFinder getArtifactFinder() {
306: return artifactFinder;
307: }
308:
309: public void setArtifactFinder(ArtifactFinder artifactFinder) {
310: this .artifactFinder = artifactFinder;
311: }
312:
313: public String getObjectStructure() {
314: return objectStructure;
315: }
316:
317: public String getRootName() {
318: return rootName;
319: }
320:
321: public void setRootName(String rootName) {
322: this .rootName = rootName;
323: }
324:
325: public void setObjectStructure(String objectStructure) {
326: this .objectStructure = objectStructure;
327: }
328:
329: public String getSupportedType() {
330: return supportedType;
331: }
332:
333: public void setSupportedType(String supportedType) {
334: this .supportedType = supportedType;
335: }
336:
337: public String getArtifactType() {
338: return artifactType;
339: }
340:
341: public void setArtifactType(String artifactType) {
342: this.artifactType = artifactType;
343: }
344: }
|