795 lines
32 KiB
Java
Executable File
795 lines
32 KiB
Java
Executable File
/*
|
|
* Copyright (C) 2003-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.installer.xml;
|
|
|
|
import com.arsdigita.cms.ContentItem;
|
|
import com.arsdigita.cms.ContentSection;
|
|
import com.arsdigita.cms.ContentType;
|
|
import com.arsdigita.cms.Folder;
|
|
import com.arsdigita.cms.util.GlobalizationUtil;
|
|
import com.arsdigita.domain.DataObjectNotFoundException;
|
|
import com.arsdigita.runtime.ConfigError;
|
|
import com.arsdigita.persistence.DataAssociation;
|
|
import com.arsdigita.util.Assert;
|
|
import com.arsdigita.util.StringUtils;
|
|
import com.arsdigita.util.UncheckedWrapperException;
|
|
import java.lang.reflect.Constructor;
|
|
import java.util.Hashtable;
|
|
import java.util.Iterator;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Stack;
|
|
import java.util.Vector;
|
|
import org.apache.log4j.Logger;
|
|
import org.apache.oro.text.perl.Perl5Util;
|
|
import org.xml.sax.Attributes;
|
|
import org.xml.sax.helpers.DefaultHandler;
|
|
|
|
/**
|
|
* Parses and XML file definition of content items in a folder.
|
|
* the XML configuration should look like this:
|
|
*
|
|
* <PRE>
|
|
* <ccm:content-items> <!-- The Document Node -->
|
|
* <ccm:folder clone="1"
|
|
* depth="3"
|
|
* label="testFolder" >
|
|
* <!-- Folders can be nested to any level. clone, depth and label are
|
|
* all required properties. Clone and depth minimum values are 1
|
|
* which result in no action. The entire nested subtree is cloned
|
|
* and replicated to the specified depth -->
|
|
* <ccm:content-item
|
|
* clone="2"
|
|
* helperClass="com.arsdigita.cms.installer.xml.GenericArticleHelper">
|
|
* <!-- ContentItems can be cloned but cannot have a depth. The
|
|
* helperClass is used to create the object described here -->
|
|
* <ccm:content-type
|
|
* classname="com.arsdigita.cms.contenttypes.Article"
|
|
* objectType="com.arsdigita.cms.contenttypes.Article"/>
|
|
* <ccm:item-properties title="ArticleItem" language="en" >
|
|
* <!-- Item properties are set here. A few tags may be supported
|
|
* example,Body-text, but the set tags are usually used to
|
|
* to call appropriate methods through the helper class on
|
|
* the ContentItem. language is an optional attribute and
|
|
* defaults to en. -->
|
|
* <ccm:body-text>
|
|
* Optional Body Text
|
|
* </ccm:body-text>
|
|
* <ccm:item-property method="setSpecies"
|
|
* argClass="java.lang.String"
|
|
* argValue="TestSpecies"/>
|
|
*
|
|
* <ccm:item-property method="setWingspan"
|
|
* argClass="java.lang.Double"
|
|
* argValue="10.0"/>
|
|
* </ccm:item-properties>
|
|
*
|
|
* <ccm:content-item
|
|
* helperClass="com.arsdigita.cms.installer.xml.ContentBundleHelper"
|
|
* association-name="birdWatch" >
|
|
* <!-- Nested ContentItems (nested within another content-item)
|
|
* are created and associated with the encapsulating item.
|
|
* These are defined as regular content-items, except for a
|
|
* few notable differences:
|
|
* 1) They must have an association-name tag that is the
|
|
* name of the encapsulating item's association.
|
|
* 2) They do not have a clone field, they are cloned as
|
|
* often as the encapsulating item.
|
|
*
|
|
* Content-items can be nested any number of times -->
|
|
* </ccm:content-item>
|
|
*
|
|
* </ccm:content-item>
|
|
* </ccm:folder>
|
|
* </ccm:content-items>
|
|
* </PRE>
|
|
*
|
|
*/
|
|
|
|
|
|
public class XMLContentItemHandler extends DefaultHandler {
|
|
private static final Logger s_log
|
|
= Logger.getLogger(XMLContentItemHandler.class);
|
|
|
|
public static final String CONTENT_ITEMS = "ccm:content-items";
|
|
public static final String CONTENT_ITEM = "ccm:content-item";
|
|
public static final String ASSOCIATED_ITEM = "ccm:associated-item";
|
|
public static final String CONTENT_TYPE = "ccm:content-type";
|
|
public static final String ITEM_PROPERTIES = "ccm:item-properties";
|
|
public static final String ITEM_PROPERTY = "ccm:item-property";
|
|
public static final String FOLDER = "ccm:folder";
|
|
public static final String BODY_TEXT = "ccm:body-text";
|
|
|
|
private xmlContentItem currItem;
|
|
|
|
private Folder m_folder;
|
|
private FolderTree currFolderTree;
|
|
private Stack associated_items;
|
|
boolean isAssociated = false;
|
|
private ContentSection m_section;
|
|
private String m_body = "foo!";
|
|
|
|
|
|
/**
|
|
* @param section the ContentSection where the items will be
|
|
* created
|
|
*/
|
|
|
|
public XMLContentItemHandler(ContentSection section) {
|
|
super();
|
|
s_log.debug(XMLContentItemHandler.class.getName());
|
|
m_section = section;
|
|
currFolderTree = new FolderTree ( null );
|
|
associated_items = new Stack();
|
|
}
|
|
|
|
|
|
public void startElement( String uri, String name,
|
|
String qName, Attributes atts) {
|
|
|
|
if (qName.equals(CONTENT_ITEMS) ) {
|
|
// Start of document do nothing
|
|
} else if (qName.equals(FOLDER)) {
|
|
/*
|
|
* Creates a new folder and sets currFolderTree to point to it.
|
|
* Adds existing tree to the FolderTree stack
|
|
*/
|
|
FolderHelper new_folderHelper = new FolderHelper ( m_section );
|
|
new_folderHelper.setCloneCount( Integer.parseInt(atts.getValue("clone")));
|
|
new_folderHelper.setName( validateTitle(atts.getValue("label")));
|
|
new_folderHelper.setDepth( Integer.parseInt(atts.getValue("depth")));
|
|
|
|
FolderTree newFolderTree = new FolderTree ( new_folderHelper );
|
|
newFolderTree.setParentTree ( currFolderTree );
|
|
currFolderTree.addSubTree ( newFolderTree );
|
|
currFolderTree = newFolderTree;
|
|
|
|
m_folder = (Folder) currFolderTree.getFolderHelper().createContentItem ( true );
|
|
|
|
} else if ( qName.equals(CONTENT_ITEM)) {
|
|
/*
|
|
* Creates a new ContentItem and points to it. (does not save
|
|
* it yet) If the item is nested within another item, it is
|
|
* created as an association and added to the association stack.
|
|
* (So that associations can be nested). Otherwise the new
|
|
* item is added to the main currFolderTree
|
|
*/
|
|
xmlContentItem newItem = new xmlContentItem ();
|
|
newItem.setHelperClass (atts.getValue("helperClass"));
|
|
newItem.setCloneCount (atts.getValue("clone"));
|
|
if ( m_folder != null ) {
|
|
newItem.getHelperClass().setParent ( m_folder );
|
|
}
|
|
|
|
if ( currItem != null ) {
|
|
if ( isAssociated ) {
|
|
associated_items.push ( currItem );
|
|
}
|
|
isAssociated = true;
|
|
|
|
newItem.setParentItem ( currItem );
|
|
final String associationName = atts.getValue ("association-name");
|
|
Assert.exists( associationName, String.class );
|
|
currItem.setAssociation ( associationName, newItem );
|
|
} else {
|
|
// only add to folderTree if not an association
|
|
currFolderTree.addContentItem ( newItem );
|
|
}
|
|
currItem = newItem;
|
|
|
|
} else if ( qName.equals(CONTENT_TYPE) ) {
|
|
String objectType = atts.getValue("objectType");
|
|
Assert.exists(objectType, String.class);
|
|
currItem.setContentType ( objectType );
|
|
} else if ( qName.equals(ITEM_PROPERTIES)) {
|
|
final String itemName = atts.getValue("title");
|
|
Assert.exists(itemName, String.class);
|
|
validateTitle(itemName);
|
|
currItem.setName ( itemName );
|
|
String l_lang = atts.getValue("language");
|
|
if ( l_lang != null && !l_lang.equals("") ) {
|
|
currItem.getHelperClass().setLanguage(l_lang);
|
|
}
|
|
currItem.create (false);
|
|
} else if ( qName.equals(BODY_TEXT)) {
|
|
s_log.warn("Begin Body text");
|
|
// do nothing
|
|
} else if ( qName.equals(ITEM_PROPERTY)) {
|
|
s_log.debug("setting property");
|
|
|
|
currItem.getHelperClass().set(
|
|
atts.getValue("method"),
|
|
atts.getValue("argClass"),
|
|
atts.getValue("argValue")
|
|
);
|
|
|
|
} else {
|
|
s_log.debug("Unknown tag: " + name);
|
|
}
|
|
}
|
|
|
|
public void characters(char[] ch,
|
|
int start,
|
|
int length) {
|
|
|
|
m_body = new String(ch, start, length);
|
|
}
|
|
|
|
public void endElement( String uri, String name,
|
|
String qName) {
|
|
|
|
if ( qName.equals(BODY_TEXT) ) {
|
|
s_log.warn("Setting body text");
|
|
currItem.getHelperClass().setBodyText(m_body);
|
|
} else if ( qName.equals(ITEM_PROPERTIES)) {
|
|
} else if ( qName.equals(CONTENT_TYPE)) {
|
|
} else if ( qName.equals(CONTENT_ITEM) ) {
|
|
/*
|
|
* Saves the item. Sets the current Item to null, or
|
|
* to the encapsulating item if it was an association
|
|
*/
|
|
currItem.save();
|
|
if ( isAssociated && ! associated_items.isEmpty() ) {
|
|
currItem = (xmlContentItem) associated_items.pop();
|
|
} else if ( isAssociated && associated_items.isEmpty() ) {
|
|
isAssociated = false;
|
|
}
|
|
|
|
if ( ! isAssociated ) {
|
|
currItem = currItem.getParentItem();
|
|
}
|
|
} else if (qName.equals(FOLDER)) {
|
|
s_log.debug ( "Reached folder end item with folderHelper: " + currFolderTree );
|
|
currFolderTree.getFolderHelper().save();
|
|
currFolderTree = currFolderTree.getParentTree();
|
|
if ( ! currFolderTree.isRoot() ) {
|
|
m_folder = (Folder) currFolderTree.getFolderHelper().create ( true );
|
|
} else {
|
|
//set this to be the root folder, null acts as default right now
|
|
m_folder = null;
|
|
}
|
|
s_log.debug ( "Done Folder endElement");
|
|
} else if (qName.equals(CONTENT_ITEMS)) {
|
|
/*
|
|
* Reached the end of the xml document. Traverse the FolderTree and
|
|
* expand out the items and folders that need to be cloned /
|
|
* replicated to a certain depth
|
|
*/
|
|
expandFolderTree ( currFolderTree, m_section.getRootFolder() );
|
|
|
|
} else {
|
|
s_log.debug("Unknown tag: " + name);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Instantiates the helper class of type <code>classname</code>
|
|
* with a single argument constructor, (section)
|
|
*/
|
|
private ContentItemHelper getHelperClass(String classname) {
|
|
|
|
if (!StringUtils.emptyString(classname)) {
|
|
try {
|
|
s_log.warn("Trying to create " + classname);
|
|
Class classDef = Class.forName(classname);
|
|
|
|
Class[] args = {ContentSection.class};
|
|
Object[] argv = { m_section };
|
|
Constructor constructor =
|
|
classDef.getConstructor(args);
|
|
s_log.warn("Got constructor " + constructor.getName());
|
|
return (ContentItemHelper)constructor.newInstance(argv);
|
|
} catch (java.lang.reflect.InvocationTargetException e) {
|
|
throw new UncheckedWrapperException(e);
|
|
} catch (InstantiationException e) {
|
|
throw new UncheckedWrapperException(e);
|
|
} catch (IllegalAccessException e) {
|
|
throw new UncheckedWrapperException(e);
|
|
} catch (ClassNotFoundException e) {
|
|
throw new UncheckedWrapperException(e);
|
|
} catch (Exception e ) {
|
|
throw new UncheckedWrapperException(e);
|
|
}
|
|
} else {
|
|
s_log.warn("Using default ContentItemHelper");
|
|
return new ContentPageHelper(m_section);
|
|
}
|
|
}
|
|
|
|
|
|
public ContentType getContentType(String typeName)
|
|
throws UncheckedWrapperException {
|
|
ContentType type = null;
|
|
s_log.debug("TypeName " + typeName);
|
|
// Look for the contenttype
|
|
try {
|
|
type = ContentType.findByAssociatedObjectType(typeName);
|
|
} catch (DataObjectNotFoundException ex) {
|
|
throw new UncheckedWrapperException(
|
|
(String) GlobalizationUtil.globalize(
|
|
"cms.installer.cannot_find_content_type")
|
|
.localize() + typeName, ex);
|
|
}
|
|
// make sure that the type is added to the section
|
|
m_section.addContentType(type);
|
|
m_section.save();
|
|
|
|
s_log.debug("Content type is: " + type.getClassName());
|
|
return type;
|
|
|
|
}
|
|
|
|
// Utilities
|
|
private String validateTitle(String name) {
|
|
Perl5Util util = new Perl5Util();
|
|
String pattern = "/[^A-Za-z_0-9\\-]+ /";
|
|
if (util.match(pattern, name)) {
|
|
throw new ConfigError(
|
|
"The \"" + name +
|
|
"\" name parameter must contain only alpha-numeric " +
|
|
"characters, underscores, and/or hyphens.");
|
|
}
|
|
return name;
|
|
}
|
|
|
|
|
|
/**
|
|
* This convenience method calls {@link #cloneFolderTree} with the parameters
|
|
* required to expand the FolderTree <code>toClone</code> with the parent
|
|
* folder <code>toAttachTo</code>.
|
|
*
|
|
* @see #cloneFolderTree
|
|
*/
|
|
protected void expandFolderTree ( FolderTree toClone, Folder toAttachTo ) {
|
|
cloneFolderTree ( toClone, toAttachTo, toAttachTo, 1, true, true, true, 1 );
|
|
}
|
|
|
|
|
|
/**
|
|
* This recursive method traverses the FolderTree <code>toClone</code>
|
|
* from top to bottom, expanding out nodes to clone, replicating nodes
|
|
* to their specified depth, and then expanding out these new trees as
|
|
* well. This is a fairly complicated method for a number of reasons, you
|
|
* should never need to call it directly, use {@link #expandFolderTree}
|
|
*
|
|
* @param toClone The FolderTree to clone and expand
|
|
* @param toAttachTo The Folder to attach the result of the expansion to
|
|
* @param parent The parent of the node to attach to
|
|
* @param cloneTime The clone number for this particular folder
|
|
* @param firstTime If this is the first time the method is being called
|
|
* . ie. not a recursive call. If so it will not attempt
|
|
* to clone toClone's root folder.
|
|
* @param mainline If the parent node is no the mainlin, ie. the orignal
|
|
* folder tree and not along one of the branches created
|
|
* via cloning or depth expansion. If it is along the
|
|
* mainline, it will not attempt to replicate it's
|
|
* subfolders.
|
|
* @param clone Whether this Folder should attempt to clone itself. False
|
|
* if it is iteslf a clone and thus should not attempt to
|
|
* clone itself
|
|
* @param depthTime The depth level that this folder is at. If it is at
|
|
* it's maximum depth, don't replicate itself as a child
|
|
* of itself any further.
|
|
*/
|
|
protected void cloneFolderTree ( FolderTree toClone,
|
|
Folder toAttachTo,
|
|
Folder parent,
|
|
int cloneTime,
|
|
boolean firstTime,
|
|
boolean mainline,
|
|
boolean clone,
|
|
int depthTime ) {
|
|
|
|
/*
|
|
* Clone self as many times as necessary. Folder's created as clones of
|
|
* this should not clone themselves.
|
|
*/
|
|
if ( clone && ! firstTime ) {
|
|
int selfNumClone = toClone.getFolderHelper().getCloneCount();
|
|
|
|
while ( cloneTime < selfNumClone ) {
|
|
Folder clonedFolder = (Folder) toClone.getFolderHelper().cloneItem (
|
|
cloneTime, parent );
|
|
copyFolderItems ( toClone, clonedFolder );
|
|
cloneTime++;
|
|
cloneFolderTree ( toClone, clonedFolder, parent, cloneTime,
|
|
false, false, false, depthTime );
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Expand yourself to the specified depth. If a depth is specified, this
|
|
* folder should replicate and expand itself as a child of itself. The
|
|
* children should remember what depth they are at. The entire subTree
|
|
* needs to be expanded
|
|
*/
|
|
if ( ! firstTime && ( depthTime < toClone.getFolderHelper().getDepth() ) ) {
|
|
depthTime++;
|
|
cloneFolderTree ( toClone, replicateFolder ( toClone, toAttachTo ),
|
|
toAttachTo, 1, false, false, true, depthTime );
|
|
}
|
|
|
|
/*
|
|
* Expand out the subfolders along all the lines, main and cloned. If on
|
|
* the mainline, grab the node to attachTo from the cloneTree itself,
|
|
* otherwise replicate the folder to attach to.
|
|
*/
|
|
List subFolders = toClone.getSubTrees();
|
|
for ( int i=0; i < subFolders.size(); i++ ) {
|
|
Folder newToAttachTo;
|
|
if ( mainline ) {
|
|
newToAttachTo = ((Folder)((FolderTree)subFolders.get(i))
|
|
.getFolderHelper().getContentItem());
|
|
} else {
|
|
newToAttachTo = replicateFolder ( (FolderTree)subFolders.get(i), toAttachTo );
|
|
}
|
|
cloneFolderTree ( (FolderTree) subFolders.get(i), newToAttachTo,
|
|
toAttachTo, 1, false, mainline, true, 1 );
|
|
}
|
|
|
|
/*
|
|
* Clone all the children for this Folder.
|
|
*/
|
|
List childItems = toClone.getContentItems();
|
|
for ( int i=0; i < childItems.size(); i++ ) {
|
|
autoCloneChild ( (xmlContentItem) childItems.get(i), toAttachTo );
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
* Clones The child the number of times it needs to be cloned and
|
|
* attaches all the children to <code>parent</code>. Cloning an
|
|
* xmlContentItem automatically clones all it's associations as well
|
|
*
|
|
* @param child the xmlContentItem to clone
|
|
* @param parent the folder to attach all the new children to
|
|
*/
|
|
private void autoCloneChild ( xmlContentItem child, Folder parent ) {
|
|
final int numClone = child.getHelperClass().getCloneCount();
|
|
for ( int i=1; i<numClone; i++ ) {
|
|
child.clone (
|
|
i,
|
|
parent,
|
|
true );
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Creates a new Folder as a child of <code>parent<code> and replicates
|
|
* all content Items (only items, not nested folders etc) of
|
|
* <code>toCopy</code> to attach to it.
|
|
*
|
|
* @param toCopy the FolderTree whose items we wish to copy to the
|
|
* new Folder returned.
|
|
* @param parent the parent of the new folder returned
|
|
* @return A child folder of parent that has replicated copies of
|
|
* all of toCopy's children
|
|
*/
|
|
private Folder replicateFolder ( FolderTree toCopy, Folder parent ) {
|
|
|
|
Folder newFolder = (Folder) toCopy.getFolderHelper().cloneItem (
|
|
toCopy.getFolderHelper().getName(), parent , true );
|
|
return copyFolderItems ( toCopy, newFolder );
|
|
}
|
|
|
|
|
|
/**
|
|
* Same as {@link #replicateFolder(FolderTree, Folder)} except that
|
|
* it does not create a new folder, but rather attaches the child
|
|
* copies to <code>copyTo</code>
|
|
*/
|
|
private Folder copyFolderItems ( FolderTree toCopy, Folder copyTo ) {
|
|
|
|
List childContent = toCopy.getContentItems();
|
|
for ( int i=0; i < childContent.size() ; i++ ) {
|
|
xmlContentItem child = (xmlContentItem) childContent.get(i);
|
|
final String name = child.getHelperClass().getName();
|
|
child.replicate(name,
|
|
copyTo,
|
|
true );
|
|
}
|
|
return copyTo;
|
|
}
|
|
|
|
|
|
private class FolderHelper extends ContentItemHelper {
|
|
int m_treeDepth;
|
|
|
|
public FolderHelper(ContentSection section) {
|
|
super(section);
|
|
m_treeDepth = 0;
|
|
setContentType(Folder.BASE_DATA_OBJECT_TYPE);
|
|
}
|
|
|
|
public void setDepth(int depth) {
|
|
m_treeDepth = depth;
|
|
s_log.debug("Depth is now " + m_treeDepth);
|
|
}
|
|
public int getDepth() {
|
|
return m_treeDepth;
|
|
}
|
|
|
|
public ContentItem createContentItem ( boolean save ) {
|
|
s_log.warn("creating folder");
|
|
Folder folder = (Folder)super.createContentItem( false );
|
|
folder.setLabel(folder.getName());
|
|
setContentItem(folder);
|
|
if ( save ) {
|
|
save ();
|
|
}
|
|
return folder;
|
|
}
|
|
|
|
public ContentItem cloneItem ( String name, Folder parent, boolean save ) {
|
|
Folder folder= (Folder)super.cloneItem(name, parent, save);
|
|
folder.setLabel(folder.getName());
|
|
folder.save();
|
|
return folder;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* This helper class acts as a Folder node that can be used to build
|
|
* a tree of folders. It maintains a copy of the FolderHelper that
|
|
* created this particular Folder, it's parent. As well as a List of
|
|
* FolderTrees that represent it's subfolders, and a List of
|
|
* xmlContentItems that represent all it's children
|
|
*/
|
|
private class FolderTree {
|
|
|
|
private List m_subFolders;
|
|
private List m_contentItems;
|
|
private FolderTree m_parentTree;
|
|
private FolderHelper m_helper;
|
|
|
|
public FolderTree ( FolderHelper helper ) {
|
|
m_subFolders = new Vector();
|
|
m_contentItems = new Vector();
|
|
m_helper = helper;
|
|
}
|
|
|
|
public FolderHelper getFolderHelper () {
|
|
return m_helper;
|
|
}
|
|
|
|
public void setParentTree ( FolderTree parentTree ) {
|
|
m_parentTree = parentTree;
|
|
if ( parentTree != null && parentTree.getFolderHelper() != null ) {
|
|
m_helper.setParent ( (Folder)parentTree.getFolderHelper().create(false) );
|
|
}
|
|
}
|
|
|
|
public FolderTree getParentTree () {
|
|
return m_parentTree;
|
|
}
|
|
|
|
public void addSubTree ( FolderTree subTree ) {
|
|
m_subFolders.add ( subTree );
|
|
}
|
|
|
|
public List getSubTrees () {
|
|
return m_subFolders;
|
|
}
|
|
|
|
public void addContentItem ( xmlContentItem item ) {
|
|
m_contentItems.add ( item );
|
|
}
|
|
|
|
public List getContentItems () {
|
|
return m_contentItems ;
|
|
}
|
|
|
|
public boolean isRoot () {
|
|
if ( m_parentTree == null ) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* This helper class is used to maintain information about each
|
|
* item As well as all it's associations. It stores the helper class
|
|
* that created the item, it's parent and lists of all associated
|
|
* xmlContentItems. Cloning this item will automatically created
|
|
* and assign clones of all it's associations as well.
|
|
*/
|
|
private class xmlContentItem {
|
|
|
|
/** The helper class that created this item */
|
|
private ContentItemHelper xml_page_helper;
|
|
/** The List of associations. Maps of association names to
|
|
* xmlContentItems */
|
|
private Vector xml_assocs;
|
|
/** The parent Item */
|
|
private xmlContentItem xml_parent_item;
|
|
|
|
public xmlContentItem () {
|
|
xml_assocs = new Vector();
|
|
}
|
|
|
|
/**
|
|
* Instantiates and sets the specified helper class
|
|
*/
|
|
public void setHelperClass ( String l_helperClass ) {
|
|
xml_page_helper = XMLContentItemHandler.this.getHelperClass (
|
|
l_helperClass );
|
|
}
|
|
|
|
public ContentItemHelper getHelperClass () {
|
|
return xml_page_helper;
|
|
}
|
|
|
|
public void setParentItem ( xmlContentItem l_parent ) {
|
|
xml_parent_item = l_parent;
|
|
}
|
|
|
|
public xmlContentItem getParentItem () {
|
|
return xml_parent_item;
|
|
}
|
|
|
|
public void setCloneCount ( String l_count ) {
|
|
xml_page_helper.setCloneCount( Integer.parseInt( l_count ) );
|
|
}
|
|
|
|
public void setContentType ( String l_objectType ) {
|
|
getHelperClass().setContentType(
|
|
(getContentType(l_objectType)).getClassName());
|
|
}
|
|
|
|
public void setName ( String l_name ) {
|
|
s_log.debug ( "Setting Name to:" + l_name );
|
|
getHelperClass().setName( l_name );
|
|
}
|
|
|
|
public void setAssociation ( String assocName, xmlContentItem assoc ) {
|
|
Map newAssoc = new Hashtable();
|
|
newAssoc.put ( assocName, assoc );
|
|
xml_assocs.add ( newAssoc );
|
|
}
|
|
|
|
public List getAssociations () {
|
|
return xml_assocs;
|
|
}
|
|
|
|
public void create ( boolean save ) {
|
|
getHelperClass().create(save);
|
|
}
|
|
|
|
/**
|
|
* Save this item. This will save all associated Items and set
|
|
* the appropriate associations as well.
|
|
*/
|
|
public void save () {
|
|
getHelperClass().save();
|
|
s_log.debug("About to save all the associations");
|
|
ContentItem theItem = getHelperClass().getContentItem();
|
|
for ( Iterator i = xml_assocs.iterator() ; i.hasNext() ; ) {
|
|
Map m_assoc = (Map) i.next();
|
|
for ( Iterator j = m_assoc.keySet().iterator() ; j.hasNext() ; ) {
|
|
String key = (String) j.next();
|
|
xmlContentItem value = (xmlContentItem) m_assoc.get(key);
|
|
DataAssociation da = (DataAssociation) theItem.get ( key );
|
|
value.getHelperClass().getContentItem().addToAssociation ( da );
|
|
s_log.debug("Just saved a data association" );
|
|
}
|
|
}
|
|
theItem.save();
|
|
}
|
|
|
|
/**
|
|
* Clones and returns the clone for this contentItem. All associated
|
|
* items are cloned and associated to the new clone.
|
|
*
|
|
* @param cloneNumber This integer is used to create the name for the
|
|
* clone returned.
|
|
* @param parent The parent of the new cloned item
|
|
* @param save If true, the new item will be saved before being returned
|
|
*/
|
|
public ContentItem clone ( int cloneNumber, Folder parent, boolean save ) {
|
|
return cloneItem ( "", cloneNumber, parent, save, false );
|
|
}
|
|
|
|
|
|
/**
|
|
* Clones and returns the clone for this contentItem. All associated
|
|
* items are cloned and associated to the new clone.
|
|
*
|
|
* @param name This will be the name of the clone returned
|
|
* @param parent The parent of the new cloned item
|
|
* @param save If true, the new item will be saved before being returned
|
|
*/
|
|
public ContentItem replicate ( String name, Folder parent, boolean save ) {
|
|
return cloneItem ( name, 0, parent, save, true );
|
|
}
|
|
|
|
|
|
/**
|
|
* FIXME: Need to refactor code here
|
|
* <P>Clones the item as well as it's associations and associates them
|
|
* appropriately.</P>
|
|
*
|
|
* @param name The name of the new clone, only used if
|
|
* <code>replicate</code> is true.
|
|
* @param cloneNumber if <code>replicate</code> is false, this will be
|
|
* used to form the new name for the clone.
|
|
* {@link #clone(int,Folder,boolean)}
|
|
* @param parent The parent of the new clone
|
|
* @param save If true, the new item will be saved before it is returned
|
|
* @param replicate will decide if the name of the clone should be based
|
|
* on <code>name</code> or <code>cloneNumber</code>
|
|
*/
|
|
private ContentItem cloneItem ( String name,
|
|
int cloneNumber,
|
|
Folder parent,
|
|
boolean save,
|
|
boolean replicate
|
|
)
|
|
{
|
|
// clone and set associations here as well
|
|
if ( replicate ) {
|
|
s_log.debug ( "About to replicate: "
|
|
+ name + " with parent: " + parent.getLabel() );
|
|
} else {
|
|
name = getHelperClass().cloneName ( cloneNumber );
|
|
s_log.debug ( "About to clone: "
|
|
+ name + " with parent: " + parent.getLabel() );
|
|
}
|
|
|
|
ContentItem clone = getHelperClass().cloneItem ( name, parent, save );
|
|
//clone each association and associate it
|
|
for ( int i=0; i < xml_assocs.size() ; i++ ) {
|
|
Map m_assoc = (Map) xml_assocs.get ( i );
|
|
for ( Iterator j = m_assoc.keySet().iterator() ; j.hasNext() ; ) {
|
|
String key = (String) j.next();
|
|
xmlContentItem value = (xmlContentItem) m_assoc.get(key);
|
|
String cloneAssocName;
|
|
if ( replicate ) {
|
|
cloneAssocName = value.getHelperClass().getName();
|
|
} else {
|
|
cloneAssocName = value.getHelperClass().cloneName(cloneNumber);
|
|
}
|
|
ContentItem cloneValue = value.getHelperClass().cloneItem (
|
|
cloneAssocName, parent, true );
|
|
|
|
s_log.debug ( "Value of item: "
|
|
+ getHelperClass().getContentItem() );
|
|
s_log.debug ( "Value of cloned item: " + clone );
|
|
s_log.debug ( "Value of association: "
|
|
+ value.getHelperClass().getContentItem() );
|
|
s_log.debug ( "Value of cloned association : " + cloneValue );
|
|
|
|
DataAssociation da = (DataAssociation) clone.get ( key );
|
|
da.clear();
|
|
cloneValue.addToAssociation ( da );
|
|
s_log.debug ("Just saved a data association" );
|
|
}
|
|
}
|
|
clone.save();
|
|
return clone;
|
|
}
|
|
}
|
|
}
|