/********************************************************************************** * $URL:https://source.sakaiproject.org/svn/osp/trunk/common/api-impl/src/java/org/theospi/portfolio/shared/model/impl/GenericXmlRenderer.java $ * $Id:GenericXmlRenderer.java 9134 2006-05-08 20:28:42Z chmaurer@iupui.edu $ *********************************************************************************** * * Copyright (c) 2005, 2006, 2007, 2008 The Sakai Foundation * * Licensed under the Educational Community License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.opensource.org/licenses/ECL-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * **********************************************************************************/ package org.theospi.portfolio.shared.model.impl; import java.beans.BeanInfo; import java.beans.IntrospectionException; import java.beans.Introspector; import java.beans.PropertyDescriptor; import java.io.InputStream; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Collection; import java.util.Iterator; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jdom.Document; import org.jdom.Element; import org.jdom.input.SAXBuilder; import org.sakaiproject.metaobj.shared.ArtifactFinder; import org.sakaiproject.metaobj.shared.EntityContextFinder; import org.sakaiproject.metaobj.shared.mgt.PresentableObjectHome; import org.sakaiproject.metaobj.shared.model.Artifact; import org.sakaiproject.metaobj.shared.model.Id; import org.sakaiproject.metaobj.utils.xml.SchemaInvalidException; import org.theospi.portfolio.shared.model.OspException; /** * This class renders an object into an xml object. This implementation * uses bean introspection to navigate the object model and convert into * a jdom model. In relies on the objectStructure xml file to specify which * properties to traverse. The prevents circlular references from being * traversed. * * Valid values for type attribute are: collection, artifact, object. * If not specified object is assumed. * * */ public class GenericXmlRenderer implements PresentableObjectHome { protected final Log logger = LogFactory.getLog(getClass()); private ArtifactFinder artifactFinder; private String objectStructure; private String rootName; private String supportedType; private String artifactType; protected Element getObjectStructureRoot(){ SAXBuilder builder = new SAXBuilder(); builder.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); // SAK-23131 InputStream is = null; try { is = getClass().getResourceAsStream(getObjectStructure()); Document doc = builder.build(is); return doc.getRootElement(); } catch (Exception e) { throw new SchemaInvalidException(e); } finally { try { is.close(); } catch (Exception e) { logger.warn("Error cleaning up resource:", e); } } } protected Element getXml(Object object, String container, String site, String context) { Element rootElement = new Element(getRootName()); try { addObjectNodeInfo(rootElement, object, getObjectStructureRoot(), container, site, context); } catch (IntrospectionException e) { logger.error("",e); } return rootElement; } protected boolean isTraversableType(PropertyDescriptor descriptor, Element structure) { if (structure == null) { return false; } if (structure.getChild(descriptor.getName()) != null || structure.getName().equals(descriptor.getName())){ return true; } return false; } protected void addObjectNodeInfo(Element parentNode, Object object, Element structure, String container, String site, String context) throws IntrospectionException { if (object == null) return; // go through each property... put each one in... logger.debug("adding object of class " + object.getClass()); BeanInfo info = Introspector.getBeanInfo(object.getClass()); PropertyDescriptor[] props = info.getPropertyDescriptors(); for (int i = 0; i < props.length; i++) { PropertyDescriptor property = props[i]; logger.debug("examining property: " + property.getName()); if (isTraversableType(property, structure)){ if (isCollection(property,structure)){ addCollectionItems(parentNode, property, object, structure, container, site, context); } else if (isArtifact(property,structure)) { addArtifactItem(parentNode, property, object, container, site, context); } else { addItem(parentNode, property, object, structure, container, site, context); } } else { addItemToXml(parentNode, property, object); } } } protected void addItemToXml(Element parentNode, PropertyDescriptor prop, Object object) { String attribName = prop.getName(); logger.debug("adding attribute: " + attribName); Method readMethod = prop.getReadMethod(); if (readMethod == null || readMethod.getParameterTypes().length > 0 || Collection.class.isAssignableFrom(readMethod.getReturnType())) { logger.debug("skipping attrib: " + attribName); return; } Element attribute = new Element(attribName); Object attribValue = null; try { attribValue = readMethod.invoke(object, (Object[]) null); } catch (IllegalAccessException e) { logger.error("could not get attribute", e); } catch (InvocationTargetException e) { logger.error("could not get attribute", e); } if (attribValue != null && attribValue.toString().length() > 0) { logger.debug("value for attrib " + attribName + " is not null."); attribute.addContent(attribValue.toString()); } parentNode.addContent(attribute); } protected void addItem(Element parentNode, PropertyDescriptor prop, Object object, Element structure, String container, String site, String context) { logger.debug("addItem()"); Method readMethod = prop.getReadMethod(); Element newElement = new Element(prop.getName()); try { Object newObject = readMethod.invoke(object, (Object[]) null); parentNode.addContent(newElement); addObjectNodeInfo(newElement, newObject, structure.getChild(prop.getName()), container, site, context); } catch (IllegalAccessException e) { logger.error("could not get attribute", e); } catch (InvocationTargetException e) { logger.error("could not get attribute", e); } catch (IntrospectionException e) { logger.error("could not get attribute", e); } } protected void addArtifactItem(Element parentNode, PropertyDescriptor prop, Object object, String container, String site, String context) { logger.debug("addArtifactItem()"); Method readMethod = prop.getReadMethod(); Id artifactId = null; try { artifactId = (Id) readMethod.invoke(object, (Object[]) null); } catch (IllegalAccessException e) { logger.error("could not get attribute", e); } catch (InvocationTargetException e) { logger.error("could not get attribute", e); } logger.debug("finding artifact with id=" + artifactId); Artifact art = getArtifactFinder().load(artifactId); if (art.getHome() instanceof PresentableObjectHome) { PresentableObjectHome home = (PresentableObjectHome) art.getHome(); Element node = home.getArtifactAsXml(art, container, site, context); node.setName("artifact"); parentNode.addContent(node); } } protected void addCollectionItems(Element parentNode, PropertyDescriptor prop, Object object, Element structure, String container, String site, String context){ logger.debug("addCollectionItems()"); Method readMethod = prop.getReadMethod(); Element newListElement = new Element(prop.getName()); try { Object newObject = readMethod.invoke(object, (Object[]) null); parentNode.addContent(newListElement); Collection items = (Collection) newObject; int index=0; for (Iterator i= items.iterator(); i.hasNext();){ Element newElement = new Element(getCollectionItemName(prop.getName())); newElement.setAttribute("index", String.valueOf(index)); newListElement.addContent(newElement); Element elementStructure = structure.getChild(prop.getName()); if (elementStructure == null && structure.getAttributeValue("isNested") != null && structure.getAttributeValue("isNested").equals("true")) { elementStructure = structure.getParentElement().getChild(prop.getName()); } addObjectNodeInfo(newElement, i.next(), elementStructure, container, site, context); index++; } } catch (IllegalAccessException e) { logger.error("could not get attribute", e); } catch (InvocationTargetException e) { logger.error("could not get attribute", e); } catch (IntrospectionException e) { logger.error("could not get attribute", e); } } protected String getCollectionItemName(String listName){ if (listName.endsWith("s")) { return listName.substring(0, listName.length() -1 ); } return listName; } protected boolean isArtifact(PropertyDescriptor prop, Element structure) { Element child = structure.getChild(prop.getName()); String typeAttribute = child.getAttributeValue("type"); if (typeAttribute != null && typeAttribute.equals("artifact")){ return true; } return false; } protected boolean isCollection(PropertyDescriptor prop, Element structure) { Element elementStructure = structure.getChild(prop.getName()); if (elementStructure == null && structure.getAttributeValue("isNested") != null && structure.getAttributeValue("isNested").equals("true")) { elementStructure = structure.getParentElement().getChild(prop.getName()); } else if (elementStructure == null) { return false; } String typeAttribute = elementStructure.getAttributeValue("type"); if (typeAttribute != null && typeAttribute.equals("collection") ){ return true; } return false; } public Element getArtifactAsXml(Artifact artifact) { return getArtifactAsXml(artifact, null, null, null); } public Element getArtifactAsXml(Artifact artifact, String container, String site, String context) { try { Class supportedType = Class.forName(getSupportedType()); if (supportedType.isAssignableFrom(artifact.getClass())) { return getXml(artifact, container, site, context); } } catch (ClassNotFoundException e) { throw new RuntimeException(getSupportedType() + " is not a valid class: " + e.getMessage(),e); } throw new OspException("Expecting object of type: " + getSupportedType() + " but found object of type " + artifact.getClass()); } public ArtifactFinder getArtifactFinder() { return artifactFinder; } public void setArtifactFinder(ArtifactFinder artifactFinder) { this.artifactFinder = artifactFinder; } public String getObjectStructure() { return objectStructure; } public String getRootName() { return rootName; } public void setRootName(String rootName) { this.rootName = rootName; } public void setObjectStructure(String objectStructure) { this.objectStructure = objectStructure; } public String getSupportedType() { return supportedType; } public void setSupportedType(String supportedType) { this.supportedType = supportedType; } public String getArtifactType() { return artifactType; } public void setArtifactType(String artifactType) { this.artifactType = artifactType; } }