/* * Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ package com.arsdigita.cms.ui; import com.arsdigita.bebop.PageState; import com.arsdigita.bebop.SimpleContainer; import com.arsdigita.domain.DomainObject; import com.arsdigita.domain.DomainObjectFactory; import com.arsdigita.domain.DomainServiceInterfaceExposer; import com.arsdigita.kernel.ui.DomainObjectSelectionModel; import com.arsdigita.persistence.DataAssociation; import com.arsdigita.persistence.DataAssociationCursor; import com.arsdigita.persistence.DataObject; import com.arsdigita.persistence.OID; import com.arsdigita.persistence.metadata.ObjectType; import com.arsdigita.persistence.metadata.Property; import com.arsdigita.util.Assert; import com.arsdigita.xml.Element; import org.apache.log4j.Logger; import java.util.HashSet; import java.util.Iterator; import java.util.Set; /** * @deprecated Use DomainObjectXMLRenderer instead * @see com.arsdigita.domain.DomainObjectXMLRenderer * * A Bebop component that takes a {@link DomainObject} and renders it * as XML. The XML can then be styled with XSL in order to insert the * object's properties into the page. *

* The XML generated by this component will follow the following pattern: *


 * <cms:domainObjectRenderer oid="main_object_oid">
 *   <attributeOne>foo</attributeOne>
 *   <attributeTwo>bar</attributeTwo>
 *   <roleReferenceOne oid="child_object_oid">
 *     <otherAttribute>baz<otherAttribute>
 *     ...
 *   </roleReferenceOne>
 *   ...
 * </cms:domainObjectRenderer>
 * 
* *

* The object which the DomainObjectRenderer should render is * supplied by a {@link DomainObjectSelectionModel}. Thus, an * {@link com.arsdigita.kernel.ui.ACSObjectSelectionModel}, an {@link com.arsdigita.cms.ItemSelectionModel}, or any other * subclass of {@link DomainObjectSelectionModel} can be used. For example, * *

 * String type = "com.arsdigita.kenel.User";
 * ACSObjectSelectionModel model = new ACSObjectSelectionModel (
 *   type, type, "item_id");
 * page.addGlobalStateParam(model.getStateParameter());
 * page.add(new DomainObjectRenderer(model));
 *
 * 

* Advanced notes: *

* The {@link #setDepth} method controls how detailed the XML will be. * At depth 1, only the attributes of the main object will be rendered. * At depth 2, the attributes of the main object as well as the children * of the main object will be rendered. At depth 3, the children as well as * the grandchildren will be rendered... and so on. The default depth is 2. *

* The XML-generating code is aware of loops. Any sub-object which has already * been rendered in XML will be rendered as a stub. For example: *


 * ...
 * <textAsset oid="[com.arsdigita.cms.TextAsset:42]"
 *   <content>I am the text</content>
 *   <parent oid="[com.arsdigita.cms.contenttypes.GenericPage:13]" />
 *   ...
 * </textAsset>
 * ...
 * 
* * Null values are not rendered at all in the XML. * * @author Stanislav Freidin * @version $Id: DomainObjectRenderer.java 1940 2009-05-29 07:15:05Z terry $ * */ public class DomainObjectRenderer extends SimpleContainer { private DomainObjectSelectionModel m_model; private int m_depth; public final static String CMS_XML_NS = "http://www.arsdigita.com/cms/1.0"; private static final Logger s_log = Logger.getLogger(DomainObjectRenderer.class); /** * Construct a new DomainObjectRenderer. * * @param model Supplies the domain object to be rendered */ public DomainObjectRenderer(DomainObjectSelectionModel model) { this(model, 2); } /** * Construct a new DomainObjectRenderer. * * @param model Supplies the domain object to be rendered * @param depth The object traversal will be limited to this depth */ public DomainObjectRenderer(DomainObjectSelectionModel model, int depth) { super(); m_model = model; m_depth = depth; } /** * Return the current depth. The depth is a measure of how detailed * the generated XML will be. */ public int getDepth() { return m_depth; } /** * Set a new depth. The depth is a measure of how detailed * the generated XML will be. */ public void setDepth(int depth) { Assert.isUnlocked(this); m_depth = depth; } /** * Return the current selection model, which will supply the domain object */ public DomainObjectSelectionModel getSelectionModel() { return m_model; } /** * Set the selection model which will supply the domain object * @pre model != null */ public void setSelectionModel(DomainObjectSelectionModel model) { Assert.isUnlocked(this); m_model = model; } /** * Return the current domain object * * @param state the request-specific page state */ public DomainObject getDomainObject(PageState state) { return m_model.getSelectedObject(state); } /** * Select a new domain object * * @param state the request-specific page state * @param obj the {@link DomainObject} which will be selected into the * selection model */ public void setDomainObject(PageState state, DomainObject obj) { m_model.setSelectedObject(state, obj); } /** * Render the specified object. * * @param obj the DataObject to be rendered * @param elementName the name for the current element * @param elementNameSpace the namespace for the current element * @param depthRemaining if 0, render the object stub and return. Otherwise, * recurse * @param visited a set of all previously visited objects. If the object is * already in the set, render the object stub and return. * @return the generated XML element */ protected Element generateXML( DomainObject obj, String elementName, String elementNameSpace, int depthRemaining, Set visited ) { Element node = new Element(elementName, elementNameSpace); OID oid = obj.getOID(); node.addAttribute("oid", oid.toString()); // Check for termination if(depthRemaining < 1) return node; // Check for duplicates if(visited.contains(oid)) return node; // Render visited.add(oid); ObjectType type = oid.getObjectType(); for(Iterator i = type.getProperties(); i.hasNext(); ) { Property prop = (Property) i.next(); String propName = prop.getName(); Object propValue = null; // This is a hack to get around other hacks in CMS. // The hacks deal with retrieving DataCollections of objects; // for example, // association { // ContentType[0..n] allContentTypes; // ContentType[0..n] allContentTypes2; // // retrieve allContentTypes { // ... // } // } // Trying to traverse this association will cause an error: // com.arsdigita.persistence.PersistenceException: Unable to retrieve // the associatedContentSectionsForType property of object type // ContentType because there is no event handler defined for the // retrieve associatedContentSectionsForType event. try { propValue = DomainServiceInterfaceExposer. get(obj,propName); } catch (Exception e) { s_log.error("Ignoring some random exception", e); // ignore } if(propValue != null) { // Render role references if(prop.isRole()) { int newDepth = depthRemaining - 1; if(propValue instanceof DataObject) { // 0..1 or 1..1 node.addContent (generateXML (DomainObjectFactory.newInstance ((DataObject)propValue), propName, "", newDepth, visited)); } else if(propValue instanceof DataAssociation) { // 0..N DataAssociationCursor daCursor = ((DataAssociation)propValue).getDataAssociationCursor(); while (daCursor.next()) { node.addContent (generateXML (DomainObjectFactory.newInstance (daCursor.getDataObject()), propName, "", newDepth, visited)); } } else { // Unknown property value type - do nothing } } else { // Render scalar attributes Element child = new Element(propName); child.setText(propValue.toString()); node.addContent(child); } } } return node; } /** * Generate XML for the domain object supplied by the * selection model. * * @return the generated element */ public Element generateXMLElement(PageState state) { DomainObject obj = getDomainObject(state); if(obj != null) { return generateXML( obj, "cms:domainObjectRenderer", CMS_XML_NS, getDepth(), new HashSet()); } else { return null; } } /** * Generate XML when you don't have a PageState, only the item * * @param obj the item to render * **/ public Element generateXMLElement(DomainObject obj) { return generateXML( obj, "cms:domainObjectRenderer", CMS_XML_NS, getDepth(), new HashSet()); } /** * Generate XML for the domain object supplied by the * selection model. */ public void generateXML(PageState state, Element parent) { Element e = generateXMLElement(state); if(e != null) parent.addContent(e); } }