Some performance improvements for rendering Content Items.

git-svn-id: https://svn.libreccm.org/ccm/trunk@2068 8810af33-2d31-482b-a856-94f89814c4df
master
jensp 2013-02-13 13:02:55 +00:00
parent a65452489f
commit 526c5fa1d4
6 changed files with 586 additions and 416 deletions

View File

@ -34,7 +34,7 @@ public class ContentItemXMLRenderer extends DomainObjectXMLRenderer {
private String m_keyName = ""; private String m_keyName = "";
private String m_relationAttribute = ""; private String m_relationAttribute = "";
public ContentItemXMLRenderer(Element root) { public ContentItemXMLRenderer(final Element root) {
super(root); super(root);
} }
@ -54,8 +54,7 @@ public class ContentItemXMLRenderer extends DomainObjectXMLRenderer {
if (nObj instanceof ContentBundle) { if (nObj instanceof ContentBundle) {
nObj = ((ContentBundle) obj). nObj = ((ContentBundle) obj).getInstance(GlobalizationHelper.getNegotiatedLocale(), true);
getInstance(GlobalizationHelper.getNegotiatedLocale(), true);
} }
super.walk(adapter, nObj, path, context, linkObject); super.walk(adapter, nObj, path, context, linkObject);
@ -64,22 +63,18 @@ public class ContentItemXMLRenderer extends DomainObjectXMLRenderer {
} }
@Override @Override
protected void handleAttribute(DomainObject obj, protected void handleAttribute(final DomainObject obj, final String path, final Property property) {
String path, final String propertyName = property.getName();
Property property) {
String propertyName = property.getName();
// Special handling for the isoCountryCode field in GenericAddress // Special handling for the isoCountryCode field in GenericAddress
if (obj instanceof GenericAddress) { if (obj instanceof GenericAddress && "isoCountryCode".equals(propertyName)) {
if (propertyName.equals("isoCountryCode")) { //if (propertyName.equals("isoCountryCode")) {
super.handleAttribute(obj, path, property); super.handleAttribute(obj, path, property);
final Element element = newElement(m_element, "country");
element.setText(GenericAddress.getCountryNameFromIsoCode(((GenericAddress) obj).getIsoCountryCode()));
return;
Element element = newElement(m_element, "country");
element.setText(GenericAddress.getCountryNameFromIsoCode(((GenericAddress) obj).
getIsoCountryCode()));
return;
}
} }
// Special handling for the relation attribute keys // Special handling for the relation attribute keys
@ -91,8 +86,7 @@ public class ContentItemXMLRenderer extends DomainObjectXMLRenderer {
&& ((RelationAttributeInterface) obj). && ((RelationAttributeInterface) obj).
hasRelationAttributeProperty(propertyName)) { hasRelationAttributeProperty(propertyName)) {
RelationAttributeInterface relationAttributeObject = final RelationAttributeInterface relationAttributeObject = (RelationAttributeInterface) obj;
(RelationAttributeInterface) obj;
key = relationAttributeObject.getRelationAttributeKey( key = relationAttributeObject.getRelationAttributeKey(
propertyName); propertyName);
@ -102,25 +96,23 @@ public class ContentItemXMLRenderer extends DomainObjectXMLRenderer {
if (obj instanceof LinkDomainObject if (obj instanceof LinkDomainObject
&& propertyName.equals(m_keyName)) { && propertyName.equals(m_keyName)) {
key = (String) ((LinkDomainObject) obj).get(m_keyName); key = (String) ((LinkDomainObject) obj).get(m_keyName);
} }
// Replace value of the property defined in RELATION_ATTRIBUTES string // Replace value of the property defined in RELATION_ATTRIBUTES string
// of the primary domain object with the localized String. // of the primary domain object with the localized String.
if (!key.isEmpty()) { if (!key.isEmpty()) {
logger.debug(String.format( // logger.debug(String.format(
"Getting relation attribute value for key '%s' of relation attribute '%s'", // "Getting relation attribute value for key '%s' of relation attribute '%s'",
key, m_relationAttribute)); // key, m_relationAttribute));
RelationAttributeCollection relationAttributeCollection = final RelationAttributeCollection relationAttributeCollection = new RelationAttributeCollection(
new RelationAttributeCollection(
m_relationAttribute, key); m_relationAttribute, key);
relationAttributeCollection.addLanguageFilter(GlobalizationHelper. relationAttributeCollection.addLanguageFilter(GlobalizationHelper.
getNegotiatedLocale().getLanguage()); getNegotiatedLocale().getLanguage());
if (relationAttributeCollection.size() > 0) { if (!relationAttributeCollection.isEmpty()) {
relationAttributeCollection.next(); relationAttributeCollection.next();
Element element = newElement(m_element, m_keyName); final Element element = newElement(m_element, m_keyName);
element.setText(relationAttributeCollection.getName()); element.setText(relationAttributeCollection.getName());
Element elementId = newElement(m_element, m_keyName + "Id"); final Element elementId = newElement(m_element, m_keyName + "Id");
elementId.setText(relationAttributeCollection.getKey()); elementId.setText(relationAttributeCollection.getKey());
relationAttributeCollection.close(); relationAttributeCollection.close();
} }
@ -132,31 +124,26 @@ public class ContentItemXMLRenderer extends DomainObjectXMLRenderer {
} }
@Override @Override
protected void beginAssociation(DomainObject obj, String path, protected void beginAssociation(final DomainObject obj, final String path, final Property property) {
Property property) {
super.beginAssociation(obj, path, property); super.beginAssociation(obj, path, property);
String propertyName = property.getName(); final String propertyName = property.getName();
if (obj instanceof RelationAttributeInterface if (obj instanceof RelationAttributeInterface
&& ((RelationAttributeInterface) obj).hasRelationAttributeProperty( && ((RelationAttributeInterface) obj).hasRelationAttributeProperty(
propertyName)) { propertyName)) {
RelationAttributeInterface relationAttributeObject = final RelationAttributeInterface relationAttributeObject = (RelationAttributeInterface) obj;
(RelationAttributeInterface) obj;
m_propertyName = propertyName; m_propertyName = propertyName;
m_keyName = relationAttributeObject.getRelationAttributeKeyName( m_keyName = relationAttributeObject.getRelationAttributeKeyName(propertyName);
propertyName); m_relationAttribute = relationAttributeObject.getRelationAttributeName(propertyName);
m_relationAttribute = relationAttributeObject.
getRelationAttributeName(propertyName);
} }
} }
@Override @Override
protected void endAssociation(DomainObject obj, String path, protected void endAssociation(final DomainObject obj, final String path, final Property property) {
Property property) {
m_propertyName = ""; m_propertyName = "";
m_keyName = ""; m_keyName = "";
@ -164,4 +151,5 @@ public class ContentItemXMLRenderer extends DomainObjectXMLRenderer {
super.endAssociation(obj, path, property); super.endAssociation(obj, path, property);
} }
} }

View File

@ -28,11 +28,15 @@ public class GenericPersonExtraXmlGenerator implements ExtraXMLGenerator {
} }
final GenericPerson person = (GenericPerson) item; final GenericPerson person = (GenericPerson) item;
//final long start = System.nanoTime();
final GenericPersonContactCollection contacts = person.getContacts(); final GenericPersonContactCollection contacts = person.getContacts();
//System.out.printf("Got contacts in %d ms\n", (System.nanoTime() - start) / 1000000);
final Element contactsElem = element.newChildElement("contacts"); final Element contactsElem = element.newChildElement("contacts");
while (contacts.next()) { while (contacts.next()) {
//final long start2 = System.nanoTime();
final GenericContact contact = contacts.getContact(GlobalizationHelper.getNegotiatedLocale().getLanguage()); final GenericContact contact = contacts.getContact(GlobalizationHelper.getNegotiatedLocale().getLanguage());
//System.err.printf("Got contact in %d ms from collection\n", (System.nanoTime() - start2) / 1000000);
generateContactXml( generateContactXml(
contactsElem, contactsElem,
contact, contact,
@ -43,10 +47,12 @@ public class GenericPersonExtraXmlGenerator implements ExtraXMLGenerator {
private void generateContactXml(final Element contactsElem, private void generateContactXml(final Element contactsElem,
final GenericContact contact, final GenericContact contact,
final PageState state) { final PageState state) {
//final long start = System.nanoTime();
final XmlGenerator generator = new XmlGenerator(contact); final XmlGenerator generator = new XmlGenerator(contact);
generator.setItemElemName("contact", ""); generator.setItemElemName("contact", "");
generator.addItemAttribute("contactType", contact.getContactType()); generator.addItemAttribute("contactType", contact.getContactType());
generator.generateXML(state, contactsElem, ""); generator.generateXML(state, contactsElem, "");
//System.err.printf("Generated XML for a contact in %d ms\n", (System.nanoTime() - start) / 1000000);
} }
public void addGlobalStateParams(final Page page) { public void addGlobalStateParams(final Page page) {

View File

@ -18,12 +18,10 @@
*/ */
package com.arsdigita.cms.dispatcher; package com.arsdigita.cms.dispatcher;
import com.arsdigita.bebop.Page;
import com.arsdigita.bebop.PageState; import com.arsdigita.bebop.PageState;
import com.arsdigita.cms.CMS; import com.arsdigita.cms.CMS;
import com.arsdigita.cms.ContentItem; import com.arsdigita.cms.ContentItem;
import com.arsdigita.cms.ContentItemXMLRenderer; import com.arsdigita.cms.ContentItemXMLRenderer;
import com.arsdigita.cms.ContentSection;
import com.arsdigita.cms.ExtraXMLGenerator; import com.arsdigita.cms.ExtraXMLGenerator;
import com.arsdigita.cms.SecurityManager; import com.arsdigita.cms.SecurityManager;
import com.arsdigita.cms.UserDefinedContentItem; import com.arsdigita.cms.UserDefinedContentItem;
@ -42,6 +40,7 @@ import com.arsdigita.persistence.OID;
import com.arsdigita.persistence.metadata.Property; import com.arsdigita.persistence.metadata.Property;
import com.arsdigita.util.UncheckedWrapperException; import com.arsdigita.util.UncheckedWrapperException;
import com.arsdigita.xml.Element; import com.arsdigita.xml.Element;
import java.util.HashMap;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
import java.util.Iterator; import java.util.Iterator;
@ -57,10 +56,8 @@ import java.util.Map;
*/ */
public class SimpleXMLGenerator implements XMLGenerator { public class SimpleXMLGenerator implements XMLGenerator {
private static final Logger s_log = private static final Logger s_log = Logger.getLogger(SimpleXMLGenerator.class);
Logger.getLogger(SimpleXMLGenerator.class); public static final String ADAPTER_CONTEXT = SimpleXMLGenerator.class.getName();
public static final String ADAPTER_CONTEXT = SimpleXMLGenerator.class.
getName();
/** /**
* jensp 2011-10-23: Sometimes the extra XML is not needed, for example * jensp 2011-10-23: Sometimes the extra XML is not needed, for example
* when embedding the XML of a content item into the XML output of another * when embedding the XML of a content item into the XML output of another
@ -79,20 +76,20 @@ public class SimpleXMLGenerator implements XMLGenerator {
/** /**
* Extra attributes for the cms:item element. * Extra attributes for the cms:item element.
*/ */
private Map<String, String> itemAttributes = private final Map<String, String> itemAttributes = new LinkedHashMap<String, String>();
new LinkedHashMap<String, String>();
/** /**
* Allows to overwrite the name and the namespace of the XML element * Allows to overwrite the name and the namespace of the XML element
* used to wrap the rendered item. * used to wrap the rendered item.
*/ */
private String itemElemName = "cms:item"; private String itemElemName = "cms:item";
private String itemElemNs = CMS.CMS_XML_NS; private String itemElemNs = CMS.CMS_XML_NS;
private static final Map<OID, Element> cache = new HashMap<OID, Element>();
private static final boolean USE_CACHE = false;
// Register general purpose adaptor for all content items // Register general purpose adaptor for all content items
static { static {
s_log.debug("Static initializer starting..."); s_log.debug("Static initializer starting...");
SimpleDomainObjectTraversalAdapter adapter = final SimpleDomainObjectTraversalAdapter adapter = new SimpleDomainObjectTraversalAdapter();
new SimpleDomainObjectTraversalAdapter();
adapter.addAssociationProperty("/object/type"); adapter.addAssociationProperty("/object/type");
adapter.addAssociationProperty("/object/categories"); adapter.addAssociationProperty("/object/categories");
@ -104,6 +101,7 @@ public class SimpleXMLGenerator implements XMLGenerator {
} }
public SimpleXMLGenerator() { public SimpleXMLGenerator() {
super();
} }
public void setUseExtraXml(final boolean useExtraXml) { public void setUseExtraXml(final boolean useExtraXml) {
@ -114,13 +112,11 @@ public class SimpleXMLGenerator implements XMLGenerator {
this.listMode = listMode; this.listMode = listMode;
} }
public void addItemAttribute(final String name, public void addItemAttribute(final String name, final String value) {
final String value) {
itemAttributes.put(name, value); itemAttributes.put(name, value);
} }
public void setItemElemName(final String itemElemName, public void setItemElemName(final String itemElemName, final String itemElemNs) {
final String itemElemNs) {
this.itemElemName = itemElemName; this.itemElemName = itemElemName;
this.itemElemNs = itemElemNs; this.itemElemNs = itemElemNs;
} }
@ -132,14 +128,12 @@ public class SimpleXMLGenerator implements XMLGenerator {
* @param parent The parent DOM element * @param parent The parent DOM element
* @param useContext The use context * @param useContext The use context
*/ */
public void generateXML(PageState state, Element parent, String useContext) { public void generateXML(final PageState state, final Element parent, final String useContext) {
final long start = System.nanoTime(); final long start = System.nanoTime();
//ContentSection section = CMS.getContext().getContentSection(); //ContentSection section = CMS.getContext().getContentSection();
ContentItem item = getContentItem(state); ContentItem item = getContentItem(state);
System.out.printf("After getting contentitem: %d ms\n", (System.nanoTime() - start) / 1000000);
s_log.info("Generate XML for item " + item.getOID()); s_log.info("Generate XML for item " + item.getOID());
Party currentParty = Kernel.getContext().getParty(); Party currentParty = Kernel.getContext().getParty();
@ -152,29 +146,24 @@ public class SimpleXMLGenerator implements XMLGenerator {
// Note that the xml that is generated is only of use if you DO NOT CACHE content pages. // Note that the xml that is generated is only of use if you DO NOT CACHE content pages.
// cg. // cg.
PermissionDescriptor edit = final PermissionDescriptor edit = new PermissionDescriptor(
new PermissionDescriptor(PrivilegeDescriptor.get( PrivilegeDescriptor.get(SecurityManager.CMS_EDIT_ITEM), item, currentParty);
SecurityManager.CMS_EDIT_ITEM), item, currentParty);
if (PermissionService.checkPermission(edit)) { if (PermissionService.checkPermission(edit)) {
parent.addAttribute("canEdit", "true"); parent.addAttribute("canEdit", "true");
} }
PermissionDescriptor publish = final PermissionDescriptor publish = new PermissionDescriptor(
new PermissionDescriptor(PrivilegeDescriptor.get( PrivilegeDescriptor.get(SecurityManager.CMS_PUBLISH), item, currentParty);
SecurityManager.CMS_PUBLISH), item, currentParty);
if (PermissionService.checkPermission(publish)) { if (PermissionService.checkPermission(publish)) {
parent.addAttribute("canPublish", "true"); parent.addAttribute("canPublish", "true");
} }
String className = item.getDefaultDomainClass(); final String className = item.getDefaultDomainClass();
System.out.printf("After creating permission attributes: %d ms\n", (System.nanoTime() - start) / 1000000);
// Ensure correct subtype of ContentItem is instantiated // Ensure correct subtype of ContentItem is instantiated
if (!item.getClass().getName().equals(className)) { if (!item.getClass().getName().equals(className)) {
s_log.info("Specializing item"); s_log.info("Specializing item");
try { try {
item = item = (ContentItem) DomainObjectFactory.newInstance(
(ContentItem) DomainObjectFactory.newInstance(new OID(item. new OID(item.getObjectType().getQualifiedName(), item.getID()));
getObjectType().getQualifiedName(), item.getID()));
} catch (DataObjectNotFoundException ex) { } catch (DataObjectNotFoundException ex) {
throw new UncheckedWrapperException( throw new UncheckedWrapperException(
(String) GlobalizationUtil.globalize( (String) GlobalizationUtil.globalize(
@ -183,55 +172,71 @@ public class SimpleXMLGenerator implements XMLGenerator {
} }
} }
System.out.printf("After checking type: %d ms\n", (System.nanoTime() - start) / 1000000);
// Implementing XMLGenerator directly is now deprecated // Implementing XMLGenerator directly is now deprecated
if (item instanceof XMLGenerator) { if (item instanceof XMLGenerator) {
s_log.info("Item implements XMLGenerator interface"); s_log.info("Item implements XMLGenerator interface");
XMLGenerator xitem = (XMLGenerator) item; final XMLGenerator xitem = (XMLGenerator) item;
xitem.generateXML(state, parent, useContext); xitem.generateXML(state, parent, useContext);
} else if (className.equals("com.arsdigita.cms.UserDefinedContentItem")) { } else if ("com.arsdigita.cms.UserDefinedContentItem".equals(className)) {
s_log.info("Item is a user defined content item"); s_log.info("Item is a user defined content item");
UserDefinedContentItem UDItem = (UserDefinedContentItem) item; final UserDefinedContentItem UDItem = (UserDefinedContentItem) item;
generateUDItemXML(UDItem, state, parent, useContext); generateUDItemXML(UDItem, state, parent, useContext);
} else { } else {
s_log.info("Item is using DomainObjectXMLRenderer"); s_log.info("Item is using DomainObjectXMLRenderer");
// This is the preferred method // This is the preferred method
Element content = startElement(useContext, parent); //final Element content = startElement(useContext, parent);
final Element content;
if (USE_CACHE && cache.containsKey(item.getOID())) {
content = cache.get(item.getOID());
} else {
ContentItemXMLRenderer renderer = content = startElement(useContext);
new ContentItemXMLRenderer(content);
renderer.setWrapAttributes(true); final ContentItemXMLRenderer renderer = new ContentItemXMLRenderer(content);
renderer.setWrapRoot(false);
renderer.setWrapObjects(false);
renderer.setRevisitFullObject(true);
renderer.walk(item, ADAPTER_CONTEXT); renderer.setWrapAttributes(true);
renderer.setWrapRoot(false);
renderer.setWrapObjects(false);
renderer.setRevisitFullObject(true);
System.out.printf("After getting rendering standard item xml: %d ms\n", (System.nanoTime() - start) / 1000000); System.out.printf("Prepared renderer in %d ms\n", (System.nanoTime() - start)
/ 1000000);
//parent.addContent(content); renderer.walk(item, ADAPTER_CONTEXT);
/* System.out.printf("Rendered standard item xml in %d ms\n", (System.nanoTime() - start)
* 2011-08-27 jensp: Introduced to remove the annoying special templates / 1000000);
* for MultiPartArticle, SiteProxy and others. The method called
* here was already definied but not used. //parent.addContent(content);
*
* 2011-10-23 jensp: It is now possible to disable the use of /*
* extra XML. * 2011-08-27 jensp: Introduced to remove the annoying special templates
*/ * for MultiPartArticle, SiteProxy and others. The method called
if (useExtraXml) { * here was already definied but not used.
for (ExtraXMLGenerator generator : item.getExtraXMLGenerators()) { *
generator.setListMode(listMode); * 2011-10-23 jensp: It is now possible to disable the use of
generator.generateXML(item, content, state); * extra XML.
*/
final long extraXMLStart = System.nanoTime();
if (useExtraXml) {
for (ExtraXMLGenerator generator : item.getExtraXMLGenerators()) {
generator.setListMode(listMode);
generator.generateXML(item, content, state);
}
} }
System.out.
printf("Rendered ExtraXML in %d ms\n", (System.nanoTime() - extraXMLStart) / 1000000);
System.out.printf(" -----\n");
cache.put(item.getOID(), content);
} }
System.out.printf("After rendering extra xml: %d ms\n", (System.nanoTime() - start) / 1000000); parent.addContent(content);
System.out.printf("Rendered item in %d ms\n\n", (System.nanoTime() - start) / 1000000);
} }
} }
@ -244,22 +249,22 @@ public class SimpleXMLGenerator implements XMLGenerator {
* @param state The page state * @param state The page state
* @return A content item * @return A content item
*/ */
protected ContentItem getContentItem(PageState state) { protected ContentItem getContentItem(final PageState state) {
if (CMS.getContext().hasContentItem()) { if (CMS.getContext().hasContentItem()) {
return CMS.getContext().getContentItem(); return CMS.getContext().getContentItem();
} else { } else {
CMSPage page = (CMSPage) state.getPage(); final CMSPage page = (CMSPage) state.getPage();
return page.getContentItem(state); return page.getContentItem(state);
} }
} }
protected void generateUDItemXML(UserDefinedContentItem UDItem, protected void generateUDItemXML(final UserDefinedContentItem UDItem,
PageState state, final PageState state,
Element parent, final Element parent,
String useContext) { final String useContext) {
Element element = startElement(useContext, parent); final Element element = startElement(useContext, parent);
Element additionalAttrs = UDItemElement(useContext); final Element additionalAttrs = UDItemElement(useContext);
element.addAttribute("type", UDItem.getContentType().getLabel()); element.addAttribute("type", UDItem.getContentType().getLabel());
element.addAttribute("id", UDItem.getID().toString()); element.addAttribute("id", UDItem.getID().toString());
@ -267,12 +272,11 @@ public class SimpleXMLGenerator implements XMLGenerator {
element.addAttribute("title", UDItem.getTitle()); element.addAttribute("title", UDItem.getTitle());
element.addAttribute("javaClass", UDItem.getContentType().getClassName()); element.addAttribute("javaClass", UDItem.getContentType().getClassName());
DynamicObjectType dot = new DynamicObjectType( final DynamicObjectType dot = new DynamicObjectType(UDItem.getSpecificObjectType());
UDItem.getSpecificObjectType()); final Iterator declaredProperties =
Iterator declaredProperties = dot.getObjectType().getDeclaredProperties();
dot.getObjectType().getDeclaredProperties(); Property currentProperty;
Property currentProperty = null; Object value;
Object value = null;
while (declaredProperties.hasNext()) { while (declaredProperties.hasNext()) {
currentProperty = (Property) declaredProperties.next(); currentProperty = (Property) declaredProperties.next();
value = (Object) UDItem.get(currentProperty.getName()); value = (Object) UDItem.get(currentProperty.getName());
@ -292,11 +296,10 @@ public class SimpleXMLGenerator implements XMLGenerator {
} }
private Element startElement(String useContext, Element parent) { private Element startElement(final String useContext, final Element parent) {
//Element element = new Element("cms:item", CMS.CMS_XML_NS); //Element element = new Element("cms:item", CMS.CMS_XML_NS);
//final Element element = new Element(itemElemName, itemElemNs); //final Element element = new Element(itemElemName, itemElemNs);
final Element element = parent.newChildElement(itemElemName, final Element element = parent.newChildElement(itemElemName, itemElemNs);
itemElemNs);
if (useContext != null) { if (useContext != null) {
element.addAttribute("useContext", useContext); element.addAttribute("useContext", useContext);
} }
@ -308,20 +311,35 @@ public class SimpleXMLGenerator implements XMLGenerator {
return element; return element;
} }
private Element UDItemElement(String useContext) { private Element startElement(final String useContext) {
Element element = new Element("cms:UDItemAttributes", CMS.CMS_XML_NS); final Element element = new Element(itemElemName, itemElemNs);
/*
if ( useContext != null ) { if (useContext != null) {
element.addAttribute("useContext", useContext); element.addAttribute("useContext", useContext);
} }
for (Map.Entry<String, String> attr : itemAttributes.entrySet()) {
element.addAttribute(attr.getKey(), attr.getValue());
}
return element;
}
private Element UDItemElement(final String useContext) {
final Element element = new Element("cms:UDItemAttributes", CMS.CMS_XML_NS);
/*
if ( useContext != null ) {
element.addAttribute("useContext", useContext);
}
*/ */
return element; return element;
} }
private Element UDItemAttrElement(String name, String value) { private Element UDItemAttrElement(final String name, final String value) {
Element element = new Element("cms:UDItemAttribute", CMS.CMS_XML_NS); final Element element = new Element("cms:UDItemAttribute", CMS.CMS_XML_NS);
element.addAttribute("UDItemAttrName", name); element.addAttribute("UDItemAttrName", name);
element.addAttribute("UDItemAttrValue", value); element.addAttribute("UDItemAttrValue", value);
return element; return element;
} }
} }

View File

@ -18,25 +18,22 @@
*/ */
package com.arsdigita.domain; package com.arsdigita.domain;
import com.arsdigita.util.Assert;
import com.arsdigita.persistence.metadata.ObjectType;
import com.arsdigita.persistence.metadata.Property;
import com.arsdigita.persistence.DataCollection;
import com.arsdigita.persistence.DataObject;
import com.arsdigita.persistence.DataAssociation; import com.arsdigita.persistence.DataAssociation;
import com.arsdigita.persistence.DataAssociationCursor; import com.arsdigita.persistence.DataAssociationCursor;
import com.arsdigita.persistence.DataCollection;
import com.arsdigita.persistence.DataObject;
import com.arsdigita.persistence.DataQuery; import com.arsdigita.persistence.DataQuery;
import com.arsdigita.persistence.DataQueryDataCollectionAdapter; import com.arsdigita.persistence.DataQueryDataCollectionAdapter;
import com.arsdigita.persistence.OID; import com.arsdigita.persistence.OID;
import com.arsdigita.persistence.SessionManager; import com.arsdigita.persistence.SessionManager;
import com.arsdigita.persistence.metadata.ObjectType;
import com.arsdigita.persistence.metadata.Property;
import com.arsdigita.util.Assert;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.HashMap;
import java.util.HashSet;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
/** /**
@ -62,8 +59,7 @@ public abstract class DomainObjectTraversal {
private Set m_visited = new HashSet(); private Set m_visited = new HashSet();
private static Map s_adapters = new HashMap(); private static Map s_adapters = new HashMap();
private static final Logger s_log = Logger.getLogger( private static final Logger s_log = Logger.getLogger(DomainObjectTraversal.class);
DomainObjectTraversal.class);
public final static String LINK_NAME = "link"; public final static String LINK_NAME = "link";
/** /**
@ -140,10 +136,9 @@ public abstract class DomainObjectTraversal {
* *
* @param type the object type to lookup * @param type the object type to lookup
* @param context the adapter context * @param context the adapter context
* @return
*/ */
public static DomainObjectTraversalAdapter lookupAdapter( public static DomainObjectTraversalAdapter lookupAdapter(final ObjectType type, final String context) {
final ObjectType type,
final String context) {
Assert.exists(type, ObjectType.class); Assert.exists(type, ObjectType.class);
Assert.exists(context, String.class); Assert.exists(context, String.class);
if (s_log.isDebugEnabled()) { if (s_log.isDebugEnabled()) {
@ -152,8 +147,7 @@ public abstract class DomainObjectTraversal {
} }
return (DomainObjectTraversalAdapter) s_adapters.get( return (DomainObjectTraversalAdapter) s_adapters.get(new AdapterKey(type, context));
new AdapterKey(type, context));
} }
/** /**
@ -163,20 +157,20 @@ public abstract class DomainObjectTraversal {
* *
* @param type the object type to search for * @param type the object type to search for
* @param context the adapter context * @param context the adapter context
* @return
*/ */
public static DomainObjectTraversalAdapter findAdapter(ObjectType type, public static DomainObjectTraversalAdapter findAdapter(final ObjectType type, final String context) {
final String context) {
Assert.exists(type, ObjectType.class); Assert.exists(type, ObjectType.class);
Assert.exists(context, String.class); Assert.exists(context, String.class);
if (s_log.isDebugEnabled()) { if (s_log.isDebugEnabled()) {
s_log.debug("findAdapter for type " + type.getQualifiedName() s_log.debug("findAdapter for type " + type.getQualifiedName()
+ " in context " + context); + " in context " + context);
StringBuilder buf = new StringBuilder(); final StringBuilder buf = new StringBuilder();
buf.append("Adapters contain:\n"); buf.append("Adapters contain:\n");
Iterator keys = s_adapters.keySet().iterator(); final Iterator keys = s_adapters.keySet().iterator();
while (keys.hasNext()) { while (keys.hasNext()) {
Object key = keys.next(); final Object key = keys.next();
buf.append(key.toString()).append(": "); buf.append(key.toString()).append(": ");
buf.append(s_adapters.get(key).toString()).append('\n'); buf.append(s_adapters.get(key).toString()).append('\n');
} }
@ -201,10 +195,8 @@ public abstract class DomainObjectTraversal {
* @param obj the domain object to traverse * @param obj the domain object to traverse
* @param context the context for the traversal adapter * @param context the context for the traversal adapter
*/ */
public void walk(final DomainObject obj, public void walk(final DomainObject obj, final String context) {
final String context) { final DomainObjectTraversalAdapter adapter = findAdapter(obj.getObjectType(), context);
final DomainObjectTraversalAdapter adapter = findAdapter(obj.getObjectType(),
context);
if (adapter == null) { if (adapter == null) {
final String errorMsg = "No adapter for object " + obj.getOID() final String errorMsg = "No adapter for object " + obj.getOID()
+ " in context " + context; + " in context " + context;
@ -214,9 +206,7 @@ public abstract class DomainObjectTraversal {
walk(obj, context, adapter); walk(obj, context, adapter);
} }
protected void walk(final DomainObject obj, protected void walk(final DomainObject obj, final String context, final DomainObjectTraversalAdapter adapter) {
final String context,
final DomainObjectTraversalAdapter adapter) {
Assert.exists(adapter, DomainObjectTraversalAdapter.class); Assert.exists(adapter, DomainObjectTraversalAdapter.class);
walk(adapter, obj, "/object", context, null); walk(adapter, obj, "/object", context, null);
} }
@ -234,12 +224,13 @@ public abstract class DomainObjectTraversal {
final DomainObject linkObject) { final DomainObject linkObject) {
s_log.debug(String.format("Walking with path %s and context %s...", path, s_log.debug(String.format("Walking with path %s and context %s...", path,
context)); context));
OID oid = obj.getOID(); final OID oid = obj.getOID();
OID linkOid = null; OID linkOid = null;
if (linkObject != null) { if (linkObject != null) {
linkOid = linkObject.getOID(); linkOid = linkObject.getOID();
} }
OID[] visitedKey = {oid, linkOid};
final OID[] visitedKey = {oid, linkOid};
// Prevent infinite recursion // Prevent infinite recursion
if (m_visited.contains(visitedKey)) { if (m_visited.contains(visitedKey)) {
revisitObject(obj, path); revisitObject(obj, path);
@ -261,146 +252,280 @@ public abstract class DomainObjectTraversal {
endLink(linkObject, path); endLink(linkObject, path);
} }
ObjectType type = obj.getObjectType(); final ObjectType type = obj.getObjectType();
// Test all properties against the traversal xml // Test all properties against the traversal xml
for (Iterator i = type.getProperties(); i.hasNext();) { for (Iterator i = type.getProperties(); i.hasNext();) {
Property prop = (Property) i.next(); processProperty((Property) i.next(), obj, adapter, path, context, oid);
String propName = prop.getName(); // System.err.println("Next property...");
// final long propStart = System.nanoTime();
if (!adapter.processProperty(obj, // final Property prop = (Property) i.next();
appendToPath(path, prop.getName()), // final String propName = prop.getName();
prop, //
context)) { // if (!adapter.processProperty(obj,
if (s_log.isDebugEnabled()) { // appendToPath(path, prop.getName()),
s_log.debug("Not processing " + appendToPath(path, prop.getName()) + " in object " + oid // prop,
+ " and context " // context)) {
+ context + " with adapter " + adapter.getClass(). // if (s_log.isDebugEnabled()) {
getName()); // s_log.debug("Not processing " + appendToPath(path, prop.getName()) + " in object " + oid
} // + " and context "
continue; // + context + " with adapter " + adapter.getClass().
} // getName());
Object propValue = obj.get(propName); // }
if (propValue == null) { // continue;
if (s_log.isDebugEnabled()) { // }
s_log.debug("Object " + oid.toString() + " doesn't " //
+ "contain property " + propName); // final Object propValue = obj.get(propName);
} // if (propValue == null) {
continue; // if (s_log.isDebugEnabled()) {
} // s_log.debug("Object " + oid.toString() + " doesn't "
// + "contain property " + propName);
if (prop.isAttribute()) { // }
handleAttribute(obj, path, prop); // continue;
// }
} else if (propValue instanceof DataObject) { //
// Property is a DataObject, so start recursion // if (prop.isAttribute()) {
if (s_log.isDebugEnabled()) { // handleAttribute(obj, path, prop);
s_log.debug(prop.getName() + " is a DataObject"); //
} // } else if (propValue instanceof DataObject) {
// // Property is a DataObject, so start recursion
beginRole(obj, path, prop); // if (s_log.isDebugEnabled()) {
// s_log.debug(prop.getName() + " is a DataObject");
walk(adapter, // }
DomainObjectFactory.newInstance((DataObject) propValue), //
appendToPath(path, propName), // beginRole(obj, path, prop);
context, //
null); // walk(adapter,
// DomainObjectFactory.newInstance((DataObject) propValue),
endRole(obj, path, prop); // appendToPath(path, propName),
} else if (propValue instanceof DataAssociation) { // context,
// null);
//
// see #25808 - this hack prevents the content field of cms_files // endRole(obj, path, prop);
// (which is a blob) from being queried when all we need is a // } else if (propValue instanceof DataAssociation) {
// list of the files on an item.. // final long assocStart = System.nanoTime();
if (prop.getName().equals("fileAttachments") && !Domain.getConfig().queryBlobContentForFileAttachments()) { //
// make true a config // // see #25808 - this hack prevents the content field of cms_files
DataQuery fileAttachmentsQuery = // // (which is a blob) from being queried when all we need is a
SessionManager.getSession().retrieveQuery( // // list of the files on an item..
"com.arsdigita.cms.contentassets.fileAttachmentsQuery"); // final long checkstart = System.nanoTime();
// final boolean result = prop.getName().equals("fileAttachments") && !Domain.getConfig().
fileAttachmentsQuery.setParameter("item_id", // queryBlobContentForFileAttachments();
obj.getOID().get("id")); // System.err.printf("Checked if property is file attachment in %d ms. Result is %s\n", (System.nanoTime()
// - checkstart)
DataCollection files = new DataQueryDataCollectionAdapter( // / 1000000, Boolean.
fileAttachmentsQuery, "file"); // toString(result));
// //if (prop.getName().equals("fileAttachments") && !Domain.getConfig().queryBlobContentForFileAttachments()) {
while (files.next()) { // if (result) {
DataObject file = files.getDataObject(); // // make true a config
walk(adapter, // DataQuery fileAttachmentsQuery =
DomainObjectFactory.newInstance(file), // SessionManager.getSession().retrieveQuery(
appendToPath(path, propName), // "com.arsdigita.cms.contentassets.fileAttachmentsQuery");
context, //
null); // fileAttachmentsQuery.setParameter("item_id",
} // obj.getOID().get("id"));
//
} else { // DataCollection files = new DataQueryDataCollectionAdapter(
//2010-11-08: Moved to seperate Methods to allow simple // fileAttachmentsQuery, "file");
//extending of the handling of data assocications //
/* // while (files.next()) {
* if (s_log.isDebugEnabled()) { s_log.debug(prop.getName() + " is a DataAssociation"); } // final DataObject file = files.getDataObject();
* beginAssociation(obj, path, prop); // walk(adapter,
* // DomainObjectFactory.newInstance(file),
* DataAssociationCursor daCursor = ((DataAssociation) propValue). getDataAssociationCursor(); // appendToPath(path, propName),
* // context,
* while (daCursor.next()) { s_log.debug("Processing data assoication cursor..."); DataObject link = // null);
* daCursor.getLink(); DomainObject linkObj = null; if (link != null) { linkObj = new // }
* LinkDomainObject(link); } walk(adapter, DomainObjectFactory.newInstance(daCursor. //
* getDataObject()), appendToPath(path, propName), context, linkObj); } endAssociation(obj, path, prop); // } else {
*/ // //2010-11-08: Moved to seperate Methods to allow simple
// //extending of the handling of data assocications
walkDataAssociations(adapter, // /*
obj, // * if (s_log.isDebugEnabled()) { s_log.debug(prop.getName() + " is a DataAssociation"); }
path, // * beginAssociation(obj, path, prop);
context, // *
prop, // * DataAssociationCursor daCursor = ((DataAssociation) propValue). getDataAssociationCursor();
propName, // *
propValue); // * while (daCursor.next()) { s_log.debug("Processing data assoication cursor..."); DataObject link =
} // * daCursor.getLink(); DomainObject linkObj = null; if (link != null) { linkObj = new
} else { // * LinkDomainObject(link); } walk(adapter, DomainObjectFactory.newInstance(daCursor.
// Unknown property value type - do nothing // * getDataObject()), appendToPath(path, propName), context, linkObj); } endAssociation(obj, path, prop);
} // */
//
// walkDataAssociations(adapter,
// obj,
// path,
// context,
// prop,
// propName,
// propValue);
// }
// System.err.printf("Proceesed data association %s (%s) in %d ms\n", path, propName, (System.nanoTime()
// - assocStart)
// / 1000000);
// } else {
// // Unknown property value type - do nothing
// }
// System.err.printf("Processed property %s in %d ms.", propName, (System.nanoTime() - propStart) / 1000000);
// System.err.printf("Walking since %d ms\n", (System.nanoTime() - start) / 1000000);
} }
// If needed, close a surrounding tag // If needed, close a surrounding tag
endObject(obj, path); endObject(obj, path);
} }
protected void walkDataAssociations(DomainObjectTraversalAdapter adapter, private void processProperty(final Property prop,
DomainObject obj, final DomainObject obj,
String path, final DomainObjectTraversalAdapter adapter,
String context, final String path,
Property prop, final String context,
String propName, final OID oid) {
Object propValue) { final long start = System.nanoTime();
s_log.debug(String.format("%s is a DataAssociation", prop.getName())); final String propName = prop.getName();
//System.out.printf("Processing property %30s...\n", propName);
beginAssociation(obj, path, prop); if (!adapter.processProperty(obj,
appendToPath(path, prop.getName()),
DataAssociationCursor daCursor = prop,
((DataAssociation) propValue).getDataAssociationCursor(); context)) {
if (s_log.isDebugEnabled()) {
while (daCursor.next()) { s_log.debug("Not processing " + appendToPath(path, prop.getName()) + " in object " + oid
walkDataAssociation(adapter, + " and context "
obj, + context + " with adapter " + adapter.getClass().
path, getName());
context, }
propName, return;
daCursor);
} }
endAssociation(obj, path, prop); final Object propValue = obj.get(propName);
if (propValue == null) {
if (s_log.isDebugEnabled()) {
s_log.debug("Object " + oid.toString() + " doesn't "
+ "contain property " + propName);
}
return;
}
if (prop.isAttribute()) {
handleAttribute(obj, path, prop);
} else if (propValue instanceof DataObject) {
// Property is a DataObject, so start recursion
if (s_log.isDebugEnabled()) {
s_log.debug(prop.getName() + " is a DataObject");
}
beginRole(obj, path, prop);
walk(adapter,
DomainObjectFactory.newInstance((DataObject) propValue),
appendToPath(path, propName),
context,
null);
endRole(obj, path, prop);
} else if (propValue instanceof DataAssociation) {
//final long assocStart = System.nanoTime();
// see #25808 - this hack prevents the content field of cms_files
// (which is a blob) from being queried when all we need is a
// list of the files on an item..
final boolean result = "fileAttachments".equals(prop.getName())
&& !Domain.getConfig().queryBlobContentForFileAttachments();
//if (prop.getName().equals("fileAttachments") && !Domain.getConfig().queryBlobContentForFileAttachments()) {
if (result) {
// make true a config
final DataQuery fileAttachmentsQuery = SessionManager.getSession().retrieveQuery(
"com.arsdigita.cms.contentassets.fileAttachmentsQuery");
fileAttachmentsQuery.setParameter("item_id", obj.getOID().get("id"));
final DataCollection files = new DataQueryDataCollectionAdapter(fileAttachmentsQuery, "file");
//2013-02-16 jensp: Don't process the loop if there are no files...
if (!files.isEmpty()) {
while (files.next()) {
final DataObject file = files.getDataObject();
walk(adapter,
DomainObjectFactory.newInstance(file),
appendToPath(path, propName),
context,
null);
}
}
} else {
//2010-11-08: Moved to seperate Methods to allow simple
//extending of the handling of data assocications
/*
* if (s_log.isDebugEnabled()) { s_log.debug(prop.getName() + " is a DataAssociation"); }
* beginAssociation(obj, path, prop);
*
* DataAssociationCursor daCursor = ((DataAssociation) propValue). getDataAssociationCursor();
*
* while (daCursor.next()) { s_log.debug("Processing data assoication cursor..."); DataObject link =
* daCursor.getLink(); DomainObject linkObj = null; if (link != null) { linkObj = new
* LinkDomainObject(link); } walk(adapter, DomainObjectFactory.newInstance(daCursor.
* getDataObject()), appendToPath(path, propName), context, linkObj); } endAssociation(obj, path, prop);
*/
//System.out.printf("\tNeeded %3d ms to get here.\n", (System.nanoTime() - assocStart) / 1000000);
walkDataAssociations(adapter,
obj,
path,
context,
prop,
propName,
propValue);
//System.out.printf("\tNeeded %3d ms to get here.\n", (System.nanoTime() - assocStart) / 1000000);
}
//System.out.printf("\tProcssed assoc in %3d ms.\n ", (System.nanoTime() - assocStart) / 1000000);
}
System.out.printf("Processed property %30s in %3d ms.\n", propName, (System.nanoTime() - start) / 1000000);
} }
protected void walkDataAssociation(DomainObjectTraversalAdapter adapter, protected void walkDataAssociations(final DomainObjectTraversalAdapter adapter,
DomainObject obj, final DomainObject obj,
String path, final String path,
String context, final String context,
String propName, final Property prop,
DataAssociationCursor daCursor) { final String propName,
final Object propValue) {
s_log.debug(String.format("%s is a DataAssociation", prop.getName()));
final DataAssociationCursor daCursor = ((DataAssociation) propValue).getDataAssociationCursor();
//jensp 2013-02-06: Wrapped the while loop with an if to avoid a call of DataAssoiciationCursor#next() on a
//empty collection. DataAssoiciationCursor#next() tooks a serious amount of time, especially on the
//fileAttachments association (about 25 ms per item). A call of DataAssociationCursor#isEmpty() needs less than
//1 ms. If you have list with specialized items, maybe with assoication to other items, this speeds up the XML
//rendering a little bit...
//Also the beginAssoication and endAssociation methods have been moved into the if block so that they only
//created if the association is not empty
if (!daCursor.isEmpty()) {
beginAssociation(obj, path, prop);
while (daCursor.next()) {
walkDataAssociation(adapter,
obj,
path,
context,
propName,
daCursor);
}
endAssociation(obj, path, prop);
}
}
protected void walkDataAssociation(final DomainObjectTraversalAdapter adapter,
final DomainObject obj,
final String path,
final String context,
final String propName,
final DataAssociationCursor daCursor) {
s_log.debug(String.format("Processing data assoication cursor for object '%s'...", s_log.debug(String.format("Processing data assoication cursor for object '%s'...",
obj.getOID().toString())); obj.getOID().toString()));
DataObject link = daCursor.getLink(); final DataObject link = daCursor.getLink();
DomainObject linkObj = null; DomainObject linkObj = null;
if (link != null) { if (link != null) {
linkObj = new LinkDomainObject(link); linkObj = new LinkDomainObject(link);
@ -414,84 +539,104 @@ public abstract class DomainObjectTraversal {
/** /**
* Method called when the processing of an object starts * Method called when the processing of an object starts
* @param obj
* @param path
*/ */
protected abstract void beginObject(DomainObject obj, protected abstract void beginObject(DomainObject obj, String path);
String path);
/** /**
* Method called when the procesing of an object completes * Method called when the procesing of an object completes
* @param obj
* @param path
*/ */
protected abstract void endObject(DomainObject obj, protected abstract void endObject(DomainObject obj, String path);
String path);
/** /**
* Method called when the processing of a Link Object starts * Method called when the processing of a Link Object starts
* @param obj
* @param path
*/ */
protected void beginLink(DomainObject obj, String path) { protected void beginLink(final DomainObject obj, final String path) {
s_log.debug(String.format("Starting link with path = %s", path)); s_log.debug(String.format("Starting link with path = %s", path));
} }
/** /**
* Method called when the procesing of a Link Object completes * Method called when the procesing of a Link Object completes
* @param obj
* @param path
*/ */
protected void endLink(DomainObject obj, String path) { protected void endLink(final DomainObject obj, final String path) {
s_log.debug(String.format("Finished link with path = %s", path)); s_log.debug(String.format("Finished link with path = %s", path));
} }
/** /**
* Method called when a previously visited object is encountered for a second time. * Method called when a previously visited object is encountered for a second time.
* @param obj
* @param path
*/ */
protected abstract void revisitObject(DomainObject obj, protected abstract void revisitObject(final DomainObject obj, final String path);
String path);
/** /**
* Method called when an attribute is encountered * Method called when an attribute is encountered
* @param obj
* @param path
* @param property
*/ */
protected abstract void handleAttribute(DomainObject obj, protected abstract void handleAttribute(final DomainObject obj, final String path, final Property property);
String path,
Property property);
/** /**
* Method called when the processing of a role starts * Method called when the processing of a role starts
* @param obj
* @param path
* @param property
*/ */
protected abstract void beginRole(DomainObject obj, protected abstract void beginRole(DomainObject obj, String path, Property property);
String path,
Property property);
/** /**
* Method called when the procesing of a role completes * Method called when the procesing of a role completes
* @param obj
* @param path
* @param property
*/ */
protected abstract void endRole(DomainObject obj, protected abstract void endRole(DomainObject obj, String path, Property property);
String path,
Property property);
/** /**
* Method called when the processing of an association starts * Method called when the processing of an association starts
* @param obj
* @param path
* @param property
*/ */
protected abstract void beginAssociation(DomainObject obj, protected abstract void beginAssociation(DomainObject obj, String path, Property property);
String path,
Property property);
/** /**
* Method called when the procesing of an association completes * Method called when the procesing of an association completes
* @param obj
* @param path
* @param property
*/ */
protected abstract void endAssociation(DomainObject obj, protected abstract void endAssociation(DomainObject obj, String path, Property property);
String path,
Property property);
protected String appendToPath(String path, // protected String appendToPath(String path, final String name) {
String name) { // if (path.endsWith("/" + name)) {
// path = path + "+";
// } else if (!path.endsWith("/" + name + "+")) {
// path = path + "/" + name;
// }
//
// return path;
// }
protected String appendToPath(final String path, final String name) {
if (path.endsWith("/" + name)) { if (path.endsWith("/" + name)) {
path = path + "+"; return path.concat("+");
} else if (!path.endsWith("/" + name + "+")) { } else if (!path.endsWith("/" + name + "+")) {
path = path + "/" + name; return path.concat("/").concat(name);
} }
return path; return path;
} }
protected String nameFromPath(String path) { protected String nameFromPath(final String path) {
int index = path.lastIndexOf("/"); final int index = path.lastIndexOf('/');
Assert.isTrue(index >= 0, "Path starts with /"); Assert.isTrue(index >= 0, "Path starts with /");
if (path.endsWith("+")) { if (path.endsWith("+")) {
@ -501,8 +646,8 @@ public abstract class DomainObjectTraversal {
} }
} }
protected String parentFromPath(String path) { protected String parentFromPath(final String path) {
int index = path.lastIndexOf("/"); final int index = path.lastIndexOf('/');
Assert.isTrue(index >= 0, "Path starts with /"); Assert.isTrue(index >= 0, "Path starts with /");
if (index == 0) { if (index == 0) {
@ -517,8 +662,7 @@ public abstract class DomainObjectTraversal {
private final ObjectType m_type; private final ObjectType m_type;
private final String m_context; private final String m_context;
public AdapterKey(ObjectType type, public AdapterKey(final ObjectType type, final String context) {
String context) {
Assert.exists(type, ObjectType.class); Assert.exists(type, ObjectType.class);
Assert.exists(context, String.class); Assert.exists(context, String.class);
m_type = type; m_type = type;
@ -526,10 +670,10 @@ public abstract class DomainObjectTraversal {
} }
@Override @Override
public boolean equals(Object o) { public boolean equals(final Object object) {
if (o instanceof AdapterKey) { if (object instanceof AdapterKey) {
AdapterKey k = (AdapterKey) o; final AdapterKey key = (AdapterKey) object;
return k.m_type.equals(m_type) && k.m_context.equals(m_context); return key.m_type.equals(m_type) && key.m_context.equals(m_context);
} else { } else {
return false; return false;
} }
@ -544,6 +688,7 @@ public abstract class DomainObjectTraversal {
public String toString() { public String toString() {
return m_type.getQualifiedName() + ',' + m_context; return m_type.getQualifiedName() + ',' + m_context;
} }
} }
/** /**
@ -551,13 +696,14 @@ public abstract class DomainObjectTraversal {
*/ */
protected class LinkDomainObject extends DomainObject { protected class LinkDomainObject extends DomainObject {
public LinkDomainObject(DataObject object) { public LinkDomainObject(final DataObject object) {
super(object); super(object);
} }
@Override @Override
public Object get(String attr) { public Object get(final String attr) {
return super.get(attr); return super.get(attr);
} }
} }
} }

View File

@ -65,9 +65,9 @@ public class DomainObjectXMLRenderer extends DomainObjectTraversal {
* @param formatter the formatter for controlling object traversal * @param formatter the formatter for controlling object traversal
* @param context the context in which the formatter should be used * @param context the context in which the formatter should be used
*/ */
public static void registerFormatter(ObjectType type, public static void registerFormatter(final ObjectType type,
DomainObjectXMLFormatter formatter, final DomainObjectXMLFormatter formatter,
String context) { final String context) {
s_formatters.put(new AdapterKey(type, context), formatter); s_formatters.put(new AdapterKey(type, context), formatter);
} }
@ -77,8 +77,7 @@ public class DomainObjectXMLRenderer extends DomainObjectTraversal {
* @param type the object type whose items will be traversed * @param type the object type whose items will be traversed
* @param context the context in which the formatter should be used * @param context the context in which the formatter should be used
*/ */
public static void unregisterFormatter(ObjectType type, public static void unregisterFormatter(final ObjectType type, final String context) {
String context) {
s_formatters.remove(new AdapterKey(type, context)); s_formatters.remove(new AdapterKey(type, context));
} }
@ -89,9 +88,9 @@ public class DomainObjectXMLRenderer extends DomainObjectTraversal {
* @param formatter the formatter for controlling object traversal * @param formatter the formatter for controlling object traversal
* @param context the context in which the formatter should be used * @param context the context in which the formatter should be used
*/ */
public static void registerFormatter(String type, public static void registerFormatter(final String type,
DomainObjectXMLFormatter formatter, final DomainObjectXMLFormatter formatter,
String context) { final String context) {
if (s_log.isDebugEnabled()) { if (s_log.isDebugEnabled()) {
s_log.debug("Registering formatter " s_log.debug("Registering formatter "
+ formatter.getClass().getName() + " for type " + type + formatter.getClass().getName() + " for type " + type
@ -109,8 +108,8 @@ public class DomainObjectXMLRenderer extends DomainObjectTraversal {
* @param type the object type whose items will be traversed * @param type the object type whose items will be traversed
* @param context the context in which the formatter should be used * @param context the context in which the formatter should be used
*/ */
public static void unregisterFormatter(String type, public static void unregisterFormatter(final String type,
String context) { final String context) {
unregisterFormatter(MetadataRoot.getMetadataRoot().getObjectType(type), unregisterFormatter(MetadataRoot.getMetadataRoot().getObjectType(type),
context); context);
} }
@ -121,9 +120,7 @@ public class DomainObjectXMLRenderer extends DomainObjectTraversal {
* @param type the object type to lookup * @param type the object type to lookup
* @param context the formatter context * @param context the formatter context
*/ */
public static DomainObjectXMLFormatter getFormatter( public static DomainObjectXMLFormatter getFormatter(final ObjectType type, final String context) {
ObjectType type,
String context) {
return (DomainObjectXMLFormatter) s_formatters.get(new AdapterKey(type, context)); return (DomainObjectXMLFormatter) s_formatters.get(new AdapterKey(type, context));
} }
@ -134,16 +131,17 @@ public class DomainObjectXMLRenderer extends DomainObjectTraversal {
* *
* @param type the object type to search for * @param type the object type to search for
* @param context the formatter context * @param context the formatter context
* @return
*/ */
public static DomainObjectXMLFormatter findFormatter(ObjectType type, public static DomainObjectXMLFormatter findFormatter(final ObjectType type, final String context) {
String context) {
DomainObjectXMLFormatter formatter = null; DomainObjectXMLFormatter formatter = null;
while (formatter == null && type != null) { ObjectType curType = type;
formatter = getFormatter(type, context); while (formatter == null && curType != null) {
formatter = getFormatter(curType, context);
if (s_log.isDebugEnabled()) { if (s_log.isDebugEnabled()) {
s_log.debug("getFormatter(" + type + "," + context + ")=" + formatter); s_log.debug("getFormatter(" + curType + "," + context + ")=" + formatter);
} }
type = type.getSupertype(); curType = curType.getSupertype();
} }
return formatter; return formatter;
} }
@ -153,39 +151,34 @@ public class DomainObjectXMLRenderer extends DomainObjectTraversal {
* *
* @param root the XML element in which to output children * @param root the XML element in which to output children
*/ */
public DomainObjectXMLRenderer(Element root) { public DomainObjectXMLRenderer(final Element root) {
m_element = root; m_element = root;
m_objectElements = new HashMap(); m_objectElements = new HashMap();
} }
public void setNamespace(String prefix, public void setNamespace(final String prefix, final String uri) {
String uri) {
m_namespacePrefix = prefix; m_namespacePrefix = prefix;
m_namespaceURI = uri; m_namespaceURI = uri;
} }
protected Object format(DomainObject obj, protected Object format(final DomainObject obj, final String path, final Property prop, final Object value) {
String path,
Property prop,
Object value) {
if (m_formatter != null) { if (m_formatter != null) {
String propertyPath = appendToPath(path, prop.getName()); final String propertyPath = appendToPath(path, prop.getName());
Object rendered = m_formatter.format(obj, Object rendered = m_formatter.format(obj, propertyPath, prop, value);
propertyPath,
prop, value);
if (s_log.isDebugEnabled()) { if (s_log.isDebugEnabled()) {
s_log.debug("FORMAT " + obj + " m_formatter=" + m_formatter + " rendered=" + rendered); s_log.debug("FORMAT " + obj + " m_formatter=" + m_formatter + " rendered=" + rendered);
} }
if (rendered == null) { if (rendered == null) {
// try supertype formatters // try supertype formatters
ObjectType objectType = obj.getObjectType().getSupertype(); ObjectType objectType = obj.getObjectType().getSupertype();
DomainObjectXMLFormatter formatter = m_formatter; DomainObjectXMLFormatter formatter = m_formatter;
while (rendered == null && formatter != null && objectType != null) { while (rendered == null && formatter != null && objectType != null) {
formatter = findFormatter(objectType, m_context); formatter = findFormatter(objectType, m_context);
if (formatter != null) { if (formatter == null) {
rendered = formatter.format(obj, propertyPath, prop, value);
} else {
rendered = null; rendered = null;
} else {
rendered = formatter.format(obj, propertyPath, prop, value);
} }
if (s_log.isDebugEnabled()) { if (s_log.isDebugEnabled()) {
s_log.debug("FALLBACK supertype " + objectType + " formatter=" + formatter + " rendered=" s_log.debug("FALLBACK supertype " + objectType + " formatter=" + formatter + " rendered="
@ -203,15 +196,14 @@ public class DomainObjectXMLRenderer extends DomainObjectTraversal {
} }
@Override @Override
protected void walk(DomainObject obj, protected void walk(final DomainObject obj, final String context, final DomainObjectTraversalAdapter adapter) {
String context,
DomainObjectTraversalAdapter adapter) {
if (s_log.isDebugEnabled()) { if (s_log.isDebugEnabled()) {
s_log.debug("Traversing " + obj + " for context " + context + " " s_log.debug("Traversing " + obj + " for context " + context + " "
+ "using adapter " + adapter); + "using adapter " + adapter);
} }
m_formatter = findFormatter(obj.getObjectType(), context); m_formatter = findFormatter(obj.getObjectType(), context);
m_context = context; m_context = context;
if (s_log.isDebugEnabled()) { if (s_log.isDebugEnabled()) {
@ -224,8 +216,9 @@ public class DomainObjectXMLRenderer extends DomainObjectTraversal {
/** /**
* Determines XML output for root object. If set to true a separate element will be output for the root object, if * Determines XML output for root object. If set to true a separate element will be output for the root object, if
* false, then the element passed into the constructor will be used. * false, then the element passed into the constructor will be used.
* @param value
*/ */
public void setWrapRoot(boolean value) { public void setWrapRoot(final boolean value) {
m_wrapRoot = value; m_wrapRoot = value;
} }
@ -233,8 +226,9 @@ public class DomainObjectXMLRenderer extends DomainObjectTraversal {
* Determines XML output used for objects. If set to true, then a wrapper XML element will be generated for the * Determines XML output used for objects. If set to true, then a wrapper XML element will be generated for the
* association, and then individual elements generated for each object. If false then no wrapper XML element will be * association, and then individual elements generated for each object. If false then no wrapper XML element will be
* produced. * produced.
* @param value
*/ */
public void setWrapObjects(boolean value) { public void setWrapObjects(final boolean value) {
m_wrapObjects = value; m_wrapObjects = value;
} }
@ -242,7 +236,7 @@ public class DomainObjectXMLRenderer extends DomainObjectTraversal {
* Determines XML output used for scalar attributes. If set to true, then each attribute is output as a separate * Determines XML output used for scalar attributes. If set to true, then each attribute is output as a separate
* element, otherwise, attributes are output as simple attributes. * element, otherwise, attributes are output as simple attributes.
*/ */
public void setWrapAttributes(boolean value) { public void setWrapAttributes(final boolean value) {
m_wrapAttributes = value; m_wrapAttributes = value;
} }
@ -250,7 +244,7 @@ public class DomainObjectXMLRenderer extends DomainObjectTraversal {
* Determines XML output used for objects. If set to true, then repeated objects will generate full xml. If false * Determines XML output used for objects. If set to true, then repeated objects will generate full xml. If false
* then only the OID will be printed. * then only the OID will be printed.
*/ */
public void setRevisitFullObject(boolean value) { public void setRevisitFullObject(final boolean value) {
m_revisitFullObject = value; m_revisitFullObject = value;
} }
@ -266,11 +260,18 @@ public class DomainObjectXMLRenderer extends DomainObjectTraversal {
return m_wrapRoot; return m_wrapRoot;
} }
protected void beginObject(DomainObject obj, protected void beginObject(final DomainObject obj, final String path) {
String path) { //if (m_wrapRoot || !path.equals("/object")) {
if (m_wrapRoot || !path.equals("/object")) { if (m_wrapRoot || !("/object".equals(path))) {
String name = m_wrapObjects ? "object" : nameFromPath(path);
Element element = newElement(m_element, name); final String name;
if (m_wrapObjects) {
name = "object";
} else {
name = nameFromPath(path);
}
//String name = m_wrapObjects ? "object" : nameFromPath(path);
final Element element = newElement(m_element, name);
m_elements.push(m_element); m_elements.push(m_element);
m_element = element; m_element = element;
@ -281,51 +282,48 @@ public class DomainObjectXMLRenderer extends DomainObjectTraversal {
} }
} }
protected void endObject(DomainObject obj, protected void endObject(final DomainObject obj, final String path) {
String path) { //if (m_wrapRoot || !path.equals("/object")) {
if (m_wrapRoot || !path.equals("/object")) { if (m_wrapRoot || !("/object".equals(path))) {
m_element = (Element) m_elements.pop(); m_element = (Element) m_elements.pop();
} }
} }
protected void revisitObject(DomainObject obj, protected void revisitObject(final DomainObject obj, final String path) {
String path) {
Element priorElement = null; Element priorElement = null;
if (m_revisitFullObject) { if (m_revisitFullObject) {
priorElement = (Element) m_objectElements.get(obj.getOID()); priorElement = (Element) m_objectElements.get(obj.getOID());
} }
if (priorElement != null && (m_elements.search(priorElement) == -1)) { if (priorElement != null && (m_elements.search(priorElement) == -1)) {
String name = m_wrapObjects ? "object" : nameFromPath(path); final String name = m_wrapObjects ? "object" : nameFromPath(path);
Element element = newElement(m_element, name, priorElement); newElement(m_element, name, priorElement);
} else { } else {
String name = m_wrapObjects ? "object" : nameFromPath(path); final String name = m_wrapObjects ? "object" : nameFromPath(path);
Element element = newElement(m_element, name); final Element element = newElement(m_element, name);
element.addAttribute("oid", obj.getOID().toString()); element.addAttribute("oid", obj.getOID().toString());
} }
} }
protected void handleAttribute(DomainObject obj, protected void handleAttribute(final DomainObject obj, final String path, final Property property) {
String path, final String name = property.getName();
Property property) { final Object value = obj.get(name);
String name = property.getName();
Object value = obj.get(name);
if (value != null) { if (value != null) {
if (m_wrapAttributes) { if (m_wrapAttributes) {
Object formattedValue = format(obj, path, property, value); final Object formattedValue = format(obj, path, property, value);
if (formattedValue instanceof Element) { if (formattedValue instanceof Element) {
m_element.addContent((Element) formattedValue); m_element.addContent((Element) formattedValue);
} else { } else {
Element element = newElement(m_element, name); final Element element = newElement(m_element, name);
element.setText((String) format(obj, path, property, value)); element.setText((String) format(obj, path, property, value));
// Quasimodo: // Quasimodo:
// Special handling of date field, should be done somewhere else // Special handling of date field, should be done somewhere else
// but that seems to be a problem // but that seems to be a problem
if (value instanceof Date) { if (value instanceof Date) {
Date date = (Date) value; final Date date = (Date) value;
Calendar calDate = Calendar.getInstance(); final Calendar calDate = Calendar.getInstance();
calDate.setTime(date); calDate.setTime(date);
// locale-independent date output // locale-independent date output
@ -338,10 +336,10 @@ public class DomainObjectXMLRenderer extends DomainObjectTraversal {
// Quasimodo: BEGIN // Quasimodo: BEGIN
// Add attributes for date and time // Add attributes for date and time
Locale negLocale = com.arsdigita.globalization.GlobalizationHelper.getNegotiatedLocale(); final Locale negLocale = com.arsdigita.globalization.GlobalizationHelper.getNegotiatedLocale();
DateFormat dateFormatter = DateFormat.getDateInstance(DateFormat.MEDIUM, negLocale); final DateFormat dateFormatter = DateFormat.getDateInstance(DateFormat.MEDIUM, negLocale);
DateFormat longDateFormatter = DateFormat.getDateInstance(DateFormat.LONG, negLocale); final DateFormat longDateFormatter = DateFormat.getDateInstance(DateFormat.LONG, negLocale);
DateFormat timeFormatter = DateFormat.getTimeInstance(DateFormat.SHORT, negLocale); final DateFormat timeFormatter = DateFormat.getTimeInstance(DateFormat.SHORT, negLocale);
element.addAttribute("date", dateFormatter.format(date)); element.addAttribute("date", dateFormatter.format(date));
element.addAttribute("longDate", longDateFormatter.format(date)); element.addAttribute("longDate", longDateFormatter.format(date));
element.addAttribute("time", timeFormatter.format(date)); element.addAttribute("time", timeFormatter.format(date));
@ -358,37 +356,29 @@ public class DomainObjectXMLRenderer extends DomainObjectTraversal {
} }
} }
protected void beginRole(DomainObject obj, protected void beginRole(final DomainObject obj, final String path, final Property property) {
String path,
Property property) {
if (m_wrapObjects) { if (m_wrapObjects) {
Element element = newElement(m_element, property.getName()); final Element element = newElement(m_element, property.getName());
m_elements.push(m_element); m_elements.push(m_element);
m_element = element; m_element = element;
} }
} }
protected void endRole(DomainObject obj, protected void endRole(final DomainObject obj, final String path, final Property property) {
String path,
Property property) {
if (m_wrapObjects) { if (m_wrapObjects) {
m_element = (Element) m_elements.pop(); m_element = (Element) m_elements.pop();
} }
} }
protected void beginAssociation(DomainObject obj, protected void beginAssociation(final DomainObject obj, final String path, final Property property) {
String path,
Property property) {
if (m_wrapObjects) { if (m_wrapObjects) {
Element element = newElement(m_element, property.getName()); final Element element = newElement(m_element, property.getName());
m_elements.push(m_element); m_elements.push(m_element);
m_element = element; m_element = element;
} }
} }
protected void endAssociation(DomainObject obj, protected void endAssociation(final DomainObject obj, final String path, final Property property) {
String path,
Property property) {
if (m_wrapObjects) { if (m_wrapObjects) {
m_element = (Element) m_elements.pop(); m_element = (Element) m_elements.pop();
} }
@ -398,7 +388,7 @@ public class DomainObjectXMLRenderer extends DomainObjectTraversal {
return m_element; return m_element;
} }
protected void setCurrentElement(Element element) { protected void setCurrentElement(final Element element) {
m_element = element; m_element = element;
} }
@ -406,21 +396,42 @@ public class DomainObjectXMLRenderer extends DomainObjectTraversal {
return m_elements; return m_elements;
} }
protected Element newElement(Element parent, protected Element newElement(final Element parent, final String name) {
String name) { if (m_namespaceURI == null) {
return m_namespaceURI == null return parent.newChildElement(name);
? parent.newChildElement(name) } else {
: parent.newChildElement(m_namespacePrefix + ":" + name, final StringBuffer nameBuffer = new StringBuffer();
m_namespaceURI); nameBuffer.append(m_namespacePrefix);
nameBuffer.append(':');
nameBuffer.append(name);
return parent.newChildElement(name, m_namespaceURI);
}
// return m_namespaceURI == null
// ? parent.newChildElement(name)
// : parent.newChildElement(m_namespacePrefix + ":" + name,
// m_namespaceURI);
} }
protected Element newElement(Element parent, protected Element newElement(final Element parent, final String name, final Element copy) {
String name, if (m_namespaceURI == null) {
Element copy) { return parent.newChildElement(name, copy);
return m_namespaceURI == null } else {
? parent.newChildElement(name, copy) final StringBuffer nameBuffer = new StringBuffer();
: parent.newChildElement(m_namespacePrefix + ":" + name, nameBuffer.append(m_namespacePrefix);
m_namespaceURI, nameBuffer.append(':');
copy); nameBuffer.append(name);
return parent.newChildElement(name, m_namespaceURI, copy);
}
// return m_namespaceURI == null
// ? parent.newChildElement(name, copy)
// : parent.newChildElement(m_namespacePrefix + ":" + name,
// m_namespaceURI,
// copy);
} }
} }

View File

@ -21,6 +21,7 @@ package com.arsdigita.domain;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
import com.arsdigita.persistence.metadata.Property; import com.arsdigita.persistence.metadata.Property;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set;
/** /**
* This is a general purpose traversal adaptor * This is a general purpose traversal adaptor
@ -157,7 +158,7 @@ public class SimpleDomainObjectTraversalAdapter
* @param prop full path of the property to remove * @param prop full path of the property to remove
*/ */
public void removeAssociationProperty(String prop) { public void removeAssociationProperty(String prop) {
s_log.debug(String.format("Removing association property '%s'", prop)); //s_log.debug(String.format("Removing association property '%s'", prop));
m_assoc.remove(prop); m_assoc.remove(prop);
} }