/*
* Rapid Beans Framework: Document.java
*
* Copyright (C) 2009 Martin Bluemel
*
* Creation Date: 01/31/2006
*
* This program 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 3 of the License, or (at your option) any later version.
* This program 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 copies of the GNU Lesser General Public License and the
* GNU General Public License along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
package org.rapidbeans.datasource;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import org.rapidbeans.core.basic.ContainerImpl;
import org.rapidbeans.core.basic.Link;
import org.rapidbeans.core.basic.LinkFrozen;
import org.rapidbeans.core.basic.Property;
import org.rapidbeans.core.basic.PropertyCollection;
import org.rapidbeans.core.basic.RapidBean;
import org.rapidbeans.core.basic.RapidBeanState;
import org.rapidbeans.core.common.RapidBeanDeserializer;
import org.rapidbeans.core.common.RapidBeanSerializer;
import org.rapidbeans.core.event.PropertyChangeEvent;
import org.rapidbeans.core.exception.BeanDuplicateException;
import org.rapidbeans.core.exception.BeanNotFoundException;
import org.rapidbeans.core.exception.RapidBeansRuntimeException;
import org.rapidbeans.core.exception.ValidationException;
import org.rapidbeans.core.exception.ValidationInstanceAssocTwiceException;
import org.rapidbeans.core.type.TypePropertyCollection;
import org.rapidbeans.core.type.TypeRapidBean;
import org.rapidbeans.core.util.ClassHelper;
import org.rapidbeans.datasource.event.AddedEvent;
import org.rapidbeans.datasource.event.ChangedEvent;
import org.rapidbeans.datasource.event.DocumentChangeListener;
import org.rapidbeans.datasource.event.RemovedEvent;
import org.rapidbeans.datasource.query.Query;
import org.rapidbeans.presentation.ApplicationManager;
import org.rapidbeans.presentation.config.ConfigDocument;
/**
* A Document can be seen an in Memory DB of BizBEans persisted in an XML file.
*
* @author Martin Bluemel
*/
public class Document extends ContainerImpl {
/**
* The default character set if nothing is defined at all.
*/
public final String DEFAULT_CHARSET = "UTF-8";
/**
* the document name.
*/
private String name = null;
/**
* @return the document's name
*/
public String getName() {
return this.name;
}
/**
* the read only flag.
*/
private boolean readonly = false;
/**
* @return the read only flag
*/
public boolean getReadonly() {
return this.readonly;
}
/**
* @param readonly
* the read only to set
*/
public void setReadonly(final boolean readonly) {
this.readonly = readonly;
}
/**
* the document configuration name.
*/
private String configName = ConfigDocument.NAME_NO_CONFIG;
/**
* @return the document's configuration name
*/
public String getConfigName() {
return this.configName;
}
/**
* @return the document's configuration name
*/
public String getConfigNameOrName() {
if (this.configName.equals(ConfigDocument.NAME_NO_CONFIG)) {
if (this.name.startsWith("file")) {
return ConfigDocument.NAME_NO_CONFIG;
} else {
return this.name;
}
} else {
return this.configName;
}
}
/**
* the document URL.
*/
private URL url = null;
/**
* @return Returns the url.
*/
public URL getUrl() {
return this.url;
}
/**
* @param argFile
* The file to set
*/
public void setUrl(final URL argUrl) {
this.url = argUrl;
}
/**
* Equals method.
*
* @param other
* the other object
*
* @return if the URL's equal
*/
@Override
public boolean equals(final Object other) {
return this.url.equals(((Document) other).url);
}
/**
* Hash code method.
*
* @return the URL's hash code
*/
@Override
public synchronized int hashCode() {
return this.url.hashCode();
}
/**
* the document's root bean.
*/
private RapidBean root = null;
/**
* the document's identity map.
*/
private IdMap idmap = null;
/**
* the document's character encoding.
*/
private String encoding = null;
/**
* @return the document's character encoding.
*/
public String getEncoding() {
return encoding;
}
/**
* @param encoding
* the encoding to set
*/
public void setEncoding(final String encoding) {
this.encoding = encoding;
}
/**
* the change flag.
*/
private boolean changed;
/**
* @return the change flag.
*/
public boolean getChanged() {
return this.changed;
}
public void resetChanged() {
this.changed = false;
}
/**
* Map a document filename to an appropriate document name.
*
* @param docfile
* the document file
*
* @return the document name
*/
public static String mapToDocname(final File docfile) {
String docname = null;
try {
docname = new File(docfile.getCanonicalPath()).toURI().toURL().toString();
} catch (MalformedURLException e) {
throw new RapidBeansRuntimeException(e);
} catch (IOException e) {
throw new RapidBeansRuntimeException(e);
}
return docname;
}
/**
* Map a document filename to an appropriate document name.
*
* @param docurl
* the document URL
*
* @return the document name
*/
public static String mapToDocname(final URL docurl) {
return docurl.toString();
}
/**
* constructs a Document by reading the XML file. The specified file's URL
* will be new the document's name.
*
* @param rbType
* the root bean type
* @param docfile
* the XML file
*/
public Document(final File docfile) {
this(mapToDocname(docfile), null, docfile);
}
/**
* constructs a Document by reading the XML file. The specified file's URL
* will be new the document's name.
*
* @param docfile
* the XML file
*/
public Document(final TypeRapidBean rbType, final File docfile) {
this(mapToDocname(docfile), rbType, docfile);
}
/**
* constructs a Document by reading the XML file.
*
* @param docname
* the document's name
* @param docfile
* the XML file
*/
public Document(final String docname, final File docfile) {
this(docname, null, docfile);
}
/**
* constructs a Document by reading the XML file.
*
* @param docname
* the document's name
* @param rbType
* the root bean type
* @param docfile
* the XML file
*/
public Document(final String docname, final TypeRapidBean rbType, final File docfile) {
try {
this.name = docname;
this.url = RapidBeanDeserializer.urlFromFile(docfile);
final RapidBeanDeserializer deser = new RapidBeanDeserializer();
this.root = deser.loadBean(rbType, this.url);
this.encoding = deser.getEncoding();
this.init();
this.validate();
} catch (RapidBeansRuntimeException e) {
throw new RapidBeansRuntimeException("Error while deserializing document from file \""
+ docfile.getAbsolutePath() + "\"", e);
}
}
/**
* validate the document.
*/
public void validate() {
this.getRoot().validate();
}
/**
* constructs a Document by reading the XML file specified by the given URL.
*
* @param docname
* the document's name
* @param docfile
* the XML file
*/
public Document(final String docname, final URL docurl) {
this(docname, null, docurl);
}
/**
* constructs a Document by reading the XML file specified by the given URL.
*
* @param rbType
* the RapidBeanType of the new document's root bean
* @param docfile
* the XML file
*/
public Document(final TypeRapidBean rbType, final URL docurl) {
this(mapToDocname(docurl), rbType, docurl);
}
/**
* constructs a Document by reading the XML file specified by the given URL.
*
* @param docname
* the document's name
* @param rbType
* the RapidBeanType of the new document's root bean
* @param docfile
* the XML file
*/
public Document(final String docname, final TypeRapidBean rbType, final URL docurl) {
try {
this.name = docname;
this.url = docurl;
final RapidBeanDeserializer deser = new RapidBeanDeserializer();
this.root = deser.loadBean(rbType, this.url);
this.encoding = deser.getEncoding();
init();
} catch (RapidBeansRuntimeException e) {
throw new RapidBeansRuntimeException("Error while deserializing document from file \"" + docurl.toString()
+ "\"", e);
}
}
/**
* constructs a Document by reading the XML file specified by the given URL.
*
* @param docname
* the document's name
* @param rbType
* the RapidBeanType of the new document's root bean
* @param docurl
* the URL
* @param is
* input stream
*/
public Document(final String docname, final TypeRapidBean rbType, final URL docurl, final InputStream is) {
try {
this.name = docname;
this.url = docurl;
final RapidBeanDeserializer deser = new RapidBeanDeserializer();
this.root = deser.loadBean(rbType, this.url, is);
this.encoding = deser.getEncoding();
init();
} catch (RapidBeansRuntimeException e) {
throw new RapidBeansRuntimeException("Error while deserializing document from file \"" + docurl.toString()
+ "\"", e);
}
}
/**
* constructs a Document out of an existing bean which will be the
* document's root bean.
*
* @param rootBean
* the document's root
*/
public Document(final RapidBean rootBean) {
this.root = rootBean;
this.name = "document";
init();
}
/**
* constructs a Document out of an existing bean which will be the
* document's root bean.
*
* @param docname
* the document's name
* @param rootBean
* the document's root
*/
public Document(final String docname, final RapidBean rootBean) {
this.name = docname;
this.root = rootBean;
init();
}
/**
* common initialization steps.
*/
private void init() {
this.idmap = new IdMap();
final DocumentTreeVisitor visitor = new DocumentTreeVisitorInitIdMap(this, this.idmap);
this.traverseDocumentTree(0, visitor, this.root);
this.resolveFrozenLinks();
this.changed = false;
}
/**
* Resolve all the document's frozen links.
*/
public void resolveFrozenLinks() {
DocumentTreeVisitor visitor = new DocumentTreeVisitorResolveFrozenLinks(this.idmap);
this.traverseDocumentTree(0, visitor, this.root);
}
/**
* @return the document's root bean
*/
public RapidBean getRoot() {
return this.root;
}
/**
* setter.
*
* @param root
* the new root to set.
*/
protected void setRoot(RapidBean root) {
this.root = root;
}
/**
* write a bean's data to the persistent store.
*/
public void save() {
save(null, false, null);
}
/**
* write a bean's data to the persistent store.
*
* @param useEncoding
* prescribe an encoding to use for writing not matter what
* encoding the document had until now. If you leave this value
* empty the original encoding is preserved.
*
* @param forceEncoding
* <br/>
* <li>true: the given encoding will be preferred if not null</li> <li>false: the original encoding will be preferred if not null</li>
* @param useUrl
* the URL to use for writing. If it is null the file's URL will
* be automatically used.
*/
public void save(final String useEncoding, final boolean forceEncoding, final URL useUrl) {
String writeEncoding = null;
if (forceEncoding) {
writeEncoding = useEncoding;
if (writeEncoding == null) {
writeEncoding = this.encoding;
}
} else {
writeEncoding = this.encoding;
if (writeEncoding == null) {
writeEncoding = useEncoding;
}
}
if (writeEncoding == null) {
writeEncoding = DEFAULT_CHARSET;
}
if (useUrl == null) {
(new RapidBeanSerializer()).saveBean(this.root, this.url, writeEncoding);
} else {
(new RapidBeanSerializer()).saveBean(this.root, useUrl, writeEncoding);
}
this.fireDocumentSaved();
}
/**
* returns a bean's data as an XML string.
*
* @param useEncoding
* prescribe an encoding to use for writing not matter what
* encoding the document had until now. If you leave this value
* empty the original encoding is preserved.
*
* @param forceEncoding
* <br/>
* <li>true: the given encoding will be preferred if not null</li> <li>false: the original encoding will be preferred if not null</li>
*/
public String toXmlString(final String useEncoding, final boolean forceEncoding) {
String writeEncoding = null;
if (forceEncoding) {
writeEncoding = useEncoding;
if (writeEncoding == null) {
writeEncoding = this.encoding;
}
} else {
writeEncoding = this.encoding;
if (writeEncoding == null) {
writeEncoding = useEncoding;
}
}
if (writeEncoding == null) {
writeEncoding = DEFAULT_CHARSET;
}
return (new RapidBeanSerializer()).toString(this.root, writeEncoding);
}
/**
* insert (create) a new bean in the DB.
*
* @param bean
* the bean to insert
*/
public void insert(final RapidBean bean) {
this.insert(bean, false);
}
/**
* insert (create) a new bean in the DB.
*
* @param bean
* the bean to insert
* @param implicitly
* special for documents. Usually you do not explicitly insert
* beans into a document. Instead insert them implicitly by
* adding the to a parent bean. If you anyway try to insert
* explicitly the document tries to find an appropriate location
* according to the following strategy. <li>Find all composition collection properties that have the type of the bean to insert as target type.</li> <li>If there is exactly one add the bean there. Otherwise throw an appropriate exception.</li>
*/
public void insert(final RapidBean bean, final boolean implicitly) {
if (this.idmap.findBean(bean.getType().getName(), bean.getIdString()) != null) {
throw new BeanDuplicateException("messagedialog.create.duplicate", bean, "Bean \"" + bean.toString()
+ "\" already exists in document \"" + this.getName() + "\"",
new Object[] { bean.toStringGui(ApplicationManager.getApplication().getCurrentLocale()) });
}
this.fireBeanAddPre(bean);
this.idmap.insert(bean);
bean.setContainer(this);
this.fireBeanAdded(bean);
}
/**
* delete a bean reference from the document.
*
* @param bean
* the bean to insert
*/
public void delete(final RapidBean bean) {
if (this.idmap.findBean(bean.getType().getName(), bean.getIdString()) == null) {
throw new BeanNotFoundException(bean.getType().getName() + ":" + bean.getIdString());
}
if (bean.getBeanState() != RapidBeanState.deleting) {
this.fireBeanRemovePre(bean);
}
this.idmap.delete(bean);
bean.setContainer(null);
if (bean.getBeanState() != RapidBeanState.deleting) {
this.fireBeanRemoved(bean);
}
}
/**
* the collection of listeners.
*/
private Collection<DocumentChangeListener> listeners = new ArrayList<DocumentChangeListener>();
/**
* registers a new DocumentChangeListener to this document.
*
* @param listener
* the listener to register
*/
public void addDocumentChangeListener(final DocumentChangeListener listener) {
this.listeners.add(listener);
}
/**
* unregisters a DocumentChangeListener from this document.
*
* @param listener
* the listener to unregister
*/
public void removeDocumentChangeListener(final DocumentChangeListener listener) {
this.listeners.remove(listener);
}
/**
* fire the bean pre add event.
*
* @param bean
* the bean that was added (has become element of this document).
*/
public void fireBeanAddPre(final RapidBean bean) {
final AddedEvent event = new AddedEvent(bean);
for (DocumentChangeListener listener : this.listeners) {
listener.beanAddPre(event);
}
}
/**
* fire the bean added event.
*
* @param bean
* the bean that was added (has become element of this document).
*/
public void fireBeanAdded(final RapidBean bean) {
if (!this.changed) {
this.changed = true;
}
final AddedEvent event = new AddedEvent(bean);
for (DocumentChangeListener listener : this.listeners) {
listener.beanAdded(event);
}
}
/**
* fire the bean pre remove event.
*
* @param bean
* the bean that is going to be removed (has become element of
* this document).
*/
public void fireBeanRemovePre(final RapidBean bean) {
final RemovedEvent event = new RemovedEvent(bean);
for (final DocumentChangeListener listener : this.listeners) {
listener.beanRemovePre(event);
}
}
/**
* fire the bean removed event.
*
* @param bean
* the bean that was removed (has become element of this
* document).
*/
public void fireBeanRemoved(final RapidBean bean) {
if (!this.changed) {
this.changed = true;
}
final RemovedEvent event = new RemovedEvent(bean);
for (final DocumentChangeListener listener : this.listeners) {
listener.beanRemoved(event);
}
}
/**
* fire the bean before change event.
*
* @param propEvent
* the property change event
*/
public void fireBeanChangePre(PropertyChangeEvent propEvent) {
final PropertyChangeEvent[] props = { propEvent };
final ChangedEvent ce = new ChangedEvent(propEvent.getBean(), props);
for (DocumentChangeListener listener : this.listeners) {
listener.beanChangePre(ce);
}
}
/**
* fire the bean changed event.
*
* @param propEvent
* the property change event
*/
public void fireBeanChanged(final PropertyChangeEvent propEvent) {
if (!this.changed) {
this.changed = true;
}
final PropertyChangeEvent[] props = { propEvent };
final ChangedEvent ce = new ChangedEvent(propEvent.getBean(), props);
for (DocumentChangeListener listener : this.listeners) {
listener.beanChanged(ce);
}
}
/**
* fire the Document saved event.
*/
public void fireDocumentSaved() {
this.changed = false;
for (DocumentChangeListener listener : this.listeners) {
listener.documentSaved();
}
}
// finders (queries)
/**
* general query for existence of a bean by type and ID.
*
* @param typename
* the bean's type
* @param id
* the bean's ID
*
* @return true if found and false if not found
*/
public boolean contains(final String typename, final String id) {
return this.findBean(typename, id) != null;
}
/**
* general query for existence of a bean by type and ID.
*
* @param bean
* the bean
*
* @return true if found and false if not found
*/
public boolean contains(final RapidBean bean) {
return contains(bean.getType().getName(), bean.getIdString());
}
/**
* general query for a bean by type and ID.
*
* @param typename
* the bean's type
* @param id
* the bean's ID
*
* @return the bean's reference or null if not found
*/
public RapidBean findBean(final String typename, final String id) {
return this.idmap.findBean(typename, id);
}
/**
* find types of all beans stored in this DB.
*
* @return a list of strings with the typenames
*/
public Collection<String> findAllTypenames() {
return this.idmap.findAllTypenames();
}
/**
* query for all beans of a type.
*
* @param typename
* the name of the bean type for which you want to find
* instances.
*
* @return a list with all found beans
*/
public List<RapidBean> findBeansByType(final String typename) {
return this.idmap.findBeansByType(typename);
}
/**
* find a set of beans by query.
*
* @param queryString
* the query string.
*
* @return a list with all found beans
*/
public List<RapidBean> findBeansByQuery(final String queryString) {
return new Query(queryString).findBeans(this);
}
/**
* find a set of beans by query.
*
* @param query
* the query.
*
* @return a list with all found beans
*/
public List<RapidBean> findBeansByQuery(final Query query) {
return query.findBeans(this);
}
/**
* find a single bean by query. Convenience method that also parses the
* query.
*
* @param squery
* the query string.
*
* @return the bean found or null
*/
public RapidBean findBeanByQuery(final String squery) {
return new Query(squery).findBean(this);
}
/**
* find a single bean by query.
*
* @param query
* the query.
*
* @return the bean found or null
*/
public RapidBean findBeanByQuery(final Query query) {
return query.findBean(this);
}
/**
* Example Array for toArray.
*/
static final Object[] OA = new Object[0];
/**
* find the path = concatenation of relationship names.
*
* @param bean
* the bean
* @param separator
* the separator character
* @return the path of the bean
*/
public String getPath(final RapidBean bean, final char separator) {
final StringBuffer sb = new StringBuffer(this.getConfigNameOrName());
Object o = bean;
ArrayList<Property> al = new ArrayList<Property>();
while (o != null) {
if (o instanceof RapidBean) {
PropertyCollection parentProp = ((RapidBean) o).getParentProperty();
if (parentProp == null) {
o = null;
} else {
o = parentProp;
}
} else if (o instanceof PropertyCollection) {
o = ((PropertyCollection) o).getBean();
} else {
throw new RapidBeansRuntimeException("Unexpected parent class \"" + o.getClass().getName()
+ "\"for bean tree model");
}
if (o != null && o instanceof Property) {
al.add((Property) o);
}
}
// stick the objects found into an array in reverse order.
final int alSize = al.size();
for (int i = alSize - 1; i >= 0; i--) {
sb.append(separator);
final String propname = al.get(i).getType().getPropName();
sb.append(propname);
}
return sb.toString();
}
/**
* visitor interface for traversing the document tree.
*
* @author Martin Bluemel
*/
private interface DocumentTreeVisitor {
/**
* the bean processing method.
*
* @param bean
* the bean to process
* @param depth
* the depth in the object tree
*/
void processBean(int depth, RapidBean bean);
}
/**
* Initialize the documents identity map.
*
* @author Martin Bluemel
*/
private final class DocumentTreeVisitorInitIdMap implements DocumentTreeVisitor {
/**
* the document itself.
*/
private Document visitorDocument = null;
/**
* the document's identity map.
*/
private IdMap vistorIdmap = null;
/**
* constructor.
*
* @param document
* the document
* @param pool
* the pool
*/
public DocumentTreeVisitorInitIdMap(final Document document, final IdMap pool) {
this.visitorDocument = document;
this.vistorIdmap = pool;
}
/**
* the bean processor.
*
* @param depth
* the depth
* @param bean
* the bean
*/
public void processBean(final int depth, final RapidBean bean) {
this.vistorIdmap.insert(bean);
bean.setContainer(this.visitorDocument);
}
}
/**
* resolve frozen links.
*
* @author Martin Bluemel
*/
private final class DocumentTreeVisitorResolveFrozenLinks implements DocumentTreeVisitor {
/**
* the document's identity map.
*/
private IdMap vistorIdmap = null;
/**
* constructor.
*
* @param pool
* the pool
*/
public DocumentTreeVisitorResolveFrozenLinks(final IdMap pool) {
this.vistorIdmap = pool;
}
/**
* the bean processor.
*
* @param depth
* the depth in the object tree
* @param bean
* the bean
*/
@SuppressWarnings("unchecked")
public void processBean(final int depth, final RapidBean bean) {
PropertyCollection colProp;
TypePropertyCollection colPropType;
TypePropertyCollection inverseColPropType;
Collection<Link> col;
String colPropTargetTypename;
boolean suppressMultipleLinksToSameInstance;
boolean ignoreAssocTwiceException;
Class<?> colclass, inverseColclass;
for (Property prop : bean.getPropertyList()) {
if (prop instanceof PropertyCollection) {
colProp = (PropertyCollection) prop;
colPropType = (TypePropertyCollection) prop.getType();
suppressMultipleLinksToSameInstance = true;
ignoreAssocTwiceException = false;
inverseColPropType = null;
colclass = colPropType.getCollectionClass();
inverseColclass = null;
if (ClassHelper.classOf(Set.class, colclass)) {
ignoreAssocTwiceException = true;
}
if (colPropType.getInverse() != null) {
inverseColPropType = (TypePropertyCollection) colPropType.getTargetType().getPropertyType(
colPropType.getInverse());
if (inverseColPropType != null) {
if (colclass == null) {
colclass = TypePropertyCollection.getDefaultCollectionClass();
}
inverseColclass = inverseColPropType.getCollectionClass();
if (inverseColclass == null) {
inverseColclass = TypePropertyCollection.getDefaultCollectionClass();
}
if (!ClassHelper.classOf(Set.class, inverseColclass)
|| !ClassHelper.classOf(Set.class, colPropType.getCollectionClass())) {
suppressMultipleLinksToSameInstance = true;
}
}
}
if (!colPropType.isComposition()) {
colPropTargetTypename = colPropType.getTargetType().getName();
col = (Collection<Link>) colProp.getValue();
if (col != null) {
Collection<Link> newCol = colProp.createNewCollection();
RapidBean targetBean;
for (Link link : col) {
if (link instanceof LinkFrozen) {
targetBean = this.vistorIdmap.findBean(colPropTargetTypename, link.getIdString());
if (targetBean == null) {
throw new ValidationException("invalid.reference", bean,
"could not resolve reference from bean:\n" + "\""
+ bean.getType().getName() + "::" + bean.getIdString() + "\"\n"
+ "property: \"" + prop.getName() + "\"\n" + "to bean \""
+ colPropTargetTypename + "::" + link.getIdString() + "\"");
}
} else {
targetBean = (RapidBean) link;
}
if (!suppressMultipleLinksToSameInstance || !newCol.contains(targetBean)) {
newCol.add(targetBean);
}
}
try {
colProp.setValue(newCol);
} catch (ValidationInstanceAssocTwiceException e) {
if (!ignoreAssocTwiceException) {
throw e;
}
}
}
}
}
}
}
}
/**
* recursive traversal of the document tree.
*
* @param depth
* the depth in the tree
* @param visitor
* carries the bean processing template method
* @param bean
* the current tree node
*/
@SuppressWarnings("unchecked")
private void traverseDocumentTree(final int depth, final DocumentTreeVisitor visitor, final RapidBean bean) {
visitor.processBean(depth, bean);
PropertyCollection colProp;
TypePropertyCollection colPropType;
Collection<RapidBean> col;
for (Property prop : bean.getPropertyList()) {
if (prop instanceof PropertyCollection) {
colProp = (PropertyCollection) prop;
colPropType = (TypePropertyCollection) prop.getType();
if (colPropType.isComposition()) {
col = (Collection<RapidBean>) colProp.getValue();
if (col != null) {
for (RapidBean sonBean : col) {
this.traverseDocumentTree(depth + 1, visitor, sonBean);
}
}
}
}
}
}
/**
* @param configName
* the configName to set
*/
public void setConfigName(String configName) {
this.configName = configName;
}
}