mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-07-24 17:32:48 +00:00
Moving to root below branch label
git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@2005 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
* either express or implied. See the License for the specific
|
||||
* language governing permissions and limitations under the
|
||||
* License.
|
||||
*/
|
||||
package org.alfresco.repo.importer.view;
|
||||
|
||||
import org.alfresco.repo.importer.Importer;
|
||||
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
|
||||
|
||||
/**
|
||||
* Maintains state about the currently imported element.
|
||||
*
|
||||
* @author David Caruana
|
||||
*
|
||||
*/
|
||||
public class ElementContext
|
||||
{
|
||||
// Dictionary Service
|
||||
private DictionaryService dictionary;
|
||||
|
||||
// Element Name
|
||||
private QName elementName;
|
||||
|
||||
// Importer
|
||||
private Importer importer;
|
||||
|
||||
|
||||
/**
|
||||
* Construct
|
||||
*
|
||||
* @param dictionary
|
||||
* @param elementName
|
||||
* @param progress
|
||||
*/
|
||||
public ElementContext(QName elementName, DictionaryService dictionary, Importer importer)
|
||||
{
|
||||
this.elementName = elementName;
|
||||
this.dictionary = dictionary;
|
||||
this.importer = importer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the element name
|
||||
*/
|
||||
public QName getElementName()
|
||||
{
|
||||
return elementName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the dictionary service
|
||||
*/
|
||||
public DictionaryService getDictionaryService()
|
||||
{
|
||||
return dictionary;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the importer
|
||||
*/
|
||||
public Importer getImporter()
|
||||
{
|
||||
return importer;
|
||||
}
|
||||
}
|
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
* either express or implied. See the License for the specific
|
||||
* language governing permissions and limitations under the
|
||||
* License.
|
||||
*/
|
||||
package org.alfresco.repo.importer.view;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.alfresco.service.namespace.QName;
|
||||
|
||||
|
||||
/**
|
||||
* Represents View Meta Data
|
||||
*
|
||||
* @author David Caruana
|
||||
*/
|
||||
public class MetaDataContext extends ElementContext
|
||||
{
|
||||
|
||||
private Map<QName, String> properties = new HashMap<QName, String>();
|
||||
|
||||
|
||||
/**
|
||||
* Construct
|
||||
*
|
||||
* @param elementName
|
||||
* @param dictionary
|
||||
* @param importer
|
||||
*/
|
||||
public MetaDataContext(QName elementName, ElementContext context)
|
||||
{
|
||||
super(elementName, context.getDictionaryService(), context.getImporter());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set meta-data property
|
||||
*
|
||||
* @param property property name
|
||||
* @param value property value
|
||||
*/
|
||||
public void setProperty(QName property, String value)
|
||||
{
|
||||
properties.put(property, value);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get meta-data property
|
||||
*
|
||||
* @param property property name
|
||||
* @return property value
|
||||
*/
|
||||
public String getProperty(QName property)
|
||||
{
|
||||
return properties.get(property);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get all meta-data properties
|
||||
*
|
||||
* @return all meta-data properties
|
||||
*/
|
||||
public Map<QName, String> getProperties()
|
||||
{
|
||||
return properties;
|
||||
}
|
||||
|
||||
}
|
341
source/java/org/alfresco/repo/importer/view/NodeContext.java
Normal file
341
source/java/org/alfresco/repo/importer/view/NodeContext.java
Normal file
@@ -0,0 +1,341 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
* either express or implied. See the License for the specific
|
||||
* language governing permissions and limitations under the
|
||||
* License.
|
||||
*/
|
||||
package org.alfresco.repo.importer.view;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.repo.importer.ImportNode;
|
||||
import org.alfresco.service.cmr.dictionary.AspectDefinition;
|
||||
import org.alfresco.service.cmr.dictionary.AssociationDefinition;
|
||||
import org.alfresco.service.cmr.dictionary.ChildAssociationDefinition;
|
||||
import org.alfresco.service.cmr.dictionary.ClassDefinition;
|
||||
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
|
||||
import org.alfresco.service.cmr.dictionary.PropertyDefinition;
|
||||
import org.alfresco.service.cmr.dictionary.TypeDefinition;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
|
||||
|
||||
/**
|
||||
* Maintains state about the currently imported node.
|
||||
*
|
||||
* @author David Caruana
|
||||
*
|
||||
*/
|
||||
public class NodeContext extends ElementContext
|
||||
implements ImportNode
|
||||
{
|
||||
private ParentContext parentContext;
|
||||
private NodeRef nodeRef;
|
||||
private TypeDefinition typeDef;
|
||||
private String childName;
|
||||
private Map<QName, AspectDefinition> nodeAspects = new HashMap<QName, AspectDefinition>();
|
||||
private Map<QName, ChildAssociationDefinition> nodeChildAssocs = new HashMap<QName, ChildAssociationDefinition>();
|
||||
private Map<QName, Serializable> nodeProperties = new HashMap<QName, Serializable>();
|
||||
private Map<QName, DataTypeDefinition> propertyDatatypes = new HashMap<QName, DataTypeDefinition>();
|
||||
|
||||
|
||||
/**
|
||||
* Construct
|
||||
*
|
||||
* @param elementName
|
||||
* @param parentContext
|
||||
* @param typeDef
|
||||
*/
|
||||
public NodeContext(QName elementName, ParentContext parentContext, TypeDefinition typeDef)
|
||||
{
|
||||
super(elementName, parentContext.getDictionaryService(), parentContext.getImporter());
|
||||
this.parentContext = parentContext;
|
||||
this.typeDef = typeDef;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.alfresco.repo.importer.ImportNode#getParentContext()
|
||||
*/
|
||||
public ParentContext getParentContext()
|
||||
{
|
||||
return parentContext;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.alfresco.repo.importer.ImportNode#getTypeDefinition()
|
||||
*/
|
||||
public TypeDefinition getTypeDefinition()
|
||||
{
|
||||
return typeDef;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Type Definition
|
||||
*
|
||||
* @param typeDef
|
||||
*/
|
||||
public void setTypeDefinition(TypeDefinition typeDef)
|
||||
{
|
||||
this.typeDef = typeDef;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.alfresco.repo.importer.ImportNode#getNodeRef()
|
||||
*/
|
||||
public NodeRef getNodeRef()
|
||||
{
|
||||
return nodeRef;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param nodeRef the node ref
|
||||
*/
|
||||
public void setNodeRef(NodeRef nodeRef)
|
||||
{
|
||||
this.nodeRef = nodeRef;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.alfresco.repo.importer.ImportNode#getChildName()
|
||||
*/
|
||||
public String getChildName()
|
||||
{
|
||||
return childName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param childName the child name
|
||||
*/
|
||||
public void setChildName(String childName)
|
||||
{
|
||||
this.childName = childName;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds a collection property to the node
|
||||
*
|
||||
* @param property
|
||||
*/
|
||||
public void addPropertyCollection(QName property)
|
||||
{
|
||||
// Do not import properties of sys:referenceable or cm:versionable
|
||||
// TODO: Make this configurable...
|
||||
PropertyDefinition propDef = getDictionaryService().getProperty(property);
|
||||
ClassDefinition classDef = (propDef == null) ? null : propDef.getContainerClass();
|
||||
if (classDef != null)
|
||||
{
|
||||
if (classDef.getName().equals(ContentModel.ASPECT_REFERENCEABLE) ||
|
||||
classDef.getName().equals(ContentModel.ASPECT_VERSIONABLE))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// create collection and assign to property
|
||||
List<Serializable>values = new ArrayList<Serializable>();
|
||||
nodeProperties.put(property, (Serializable)values);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds a property to the node
|
||||
*
|
||||
* @param property the property name
|
||||
* @param value the property value
|
||||
*/
|
||||
public void addProperty(QName property, String value)
|
||||
{
|
||||
// Do not import properties of sys:referenceable or cm:versionable
|
||||
// TODO: Make this configurable...
|
||||
PropertyDefinition propDef = getDictionaryService().getProperty(property);
|
||||
ClassDefinition classDef = (propDef == null) ? null : propDef.getContainerClass();
|
||||
if (classDef != null)
|
||||
{
|
||||
if (classDef.getName().equals(ContentModel.ASPECT_REFERENCEABLE) ||
|
||||
classDef.getName().equals(ContentModel.ASPECT_VERSIONABLE))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle single / multi-valued cases
|
||||
Serializable newValue = value;
|
||||
Serializable existingValue = nodeProperties.get(property);
|
||||
if (existingValue != null)
|
||||
{
|
||||
if (existingValue instanceof Collection)
|
||||
{
|
||||
// add to existing collection of values
|
||||
((Collection<Serializable>)existingValue).add(value);
|
||||
newValue = existingValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
// convert single to multi-valued
|
||||
List<Serializable>values = new ArrayList<Serializable>();
|
||||
values.add((String)existingValue);
|
||||
values.add(value);
|
||||
newValue = (Serializable)values;
|
||||
}
|
||||
}
|
||||
nodeProperties.put(property, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a property datatype to the node
|
||||
*
|
||||
* @param property property name
|
||||
* @param datatype property datatype
|
||||
*/
|
||||
public void addDatatype(QName property, DataTypeDefinition datatype)
|
||||
{
|
||||
propertyDatatypes.put(property, datatype);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.alfresco.repo.importer.ImportNode#getPropertyDatatypes()
|
||||
*/
|
||||
public Map<QName, DataTypeDefinition> getPropertyDatatypes()
|
||||
{
|
||||
return propertyDatatypes;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.alfresco.repo.importer.ImportNode#getProperties()
|
||||
*/
|
||||
public Map<QName, Serializable> getProperties()
|
||||
{
|
||||
return nodeProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an aspect to the node
|
||||
*
|
||||
* @param aspect the aspect
|
||||
*/
|
||||
public void addAspect(AspectDefinition aspect)
|
||||
{
|
||||
nodeAspects.put(aspect.getName(), aspect);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.alfresco.repo.importer.ImportNode#getNodeAspects()
|
||||
*/
|
||||
public Set<QName> getNodeAspects()
|
||||
{
|
||||
return nodeAspects.keySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the type of definition (aspect, property, association) from the
|
||||
* specified name
|
||||
*
|
||||
* @param defName
|
||||
* @return the dictionary definition
|
||||
*/
|
||||
public Object determineDefinition(QName defName)
|
||||
{
|
||||
Object def = determineAspect(defName);
|
||||
if (def == null)
|
||||
{
|
||||
def = determineProperty(defName);
|
||||
if (def == null)
|
||||
{
|
||||
def = determineAssociation(defName);
|
||||
}
|
||||
}
|
||||
return def;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if name referes to an aspect
|
||||
*
|
||||
* @param defName
|
||||
* @return
|
||||
*/
|
||||
public AspectDefinition determineAspect(QName defName)
|
||||
{
|
||||
AspectDefinition def = null;
|
||||
if (nodeAspects.containsKey(defName) == false)
|
||||
{
|
||||
def = getDictionaryService().getAspect(defName);
|
||||
}
|
||||
return def;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if name refers to a property
|
||||
*
|
||||
* @param defName
|
||||
* @return
|
||||
*/
|
||||
public PropertyDefinition determineProperty(QName defName)
|
||||
{
|
||||
PropertyDefinition def = null;
|
||||
if (nodeProperties.containsKey(defName) == false)
|
||||
{
|
||||
def = getDictionaryService().getProperty(typeDef.getName(), defName);
|
||||
if (def == null)
|
||||
{
|
||||
Set<AspectDefinition> allAspects = new HashSet<AspectDefinition>();
|
||||
allAspects.addAll(typeDef.getDefaultAspects());
|
||||
allAspects.addAll(nodeAspects.values());
|
||||
for (AspectDefinition aspectDef : allAspects)
|
||||
{
|
||||
def = getDictionaryService().getProperty(aspectDef.getName(), defName);
|
||||
if (def != null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return def;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if name referes to an association
|
||||
*
|
||||
* @param defName
|
||||
* @return
|
||||
*/
|
||||
public AssociationDefinition determineAssociation(QName defName)
|
||||
{
|
||||
AssociationDefinition def = null;
|
||||
if (nodeChildAssocs.containsKey(defName) == false)
|
||||
{
|
||||
def = getDictionaryService().getAssociation(defName);
|
||||
}
|
||||
return def;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "NodeContext[childName=" + getChildName() + ",type=" + (typeDef == null ? "null" : typeDef.getName()) + ",nodeRef=" + nodeRef +
|
||||
",aspects=" + nodeAspects.values() + ",parentContext=" + parentContext.toString() + "]";
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
* either express or implied. See the License for the specific
|
||||
* language governing permissions and limitations under the
|
||||
* License.
|
||||
*/
|
||||
package org.alfresco.repo.importer.view;
|
||||
|
||||
import org.alfresco.service.namespace.QName;
|
||||
|
||||
|
||||
/**
|
||||
* Represents Property Context
|
||||
*
|
||||
* @author David Caruana
|
||||
*
|
||||
*/
|
||||
public class NodeItemContext extends ElementContext
|
||||
{
|
||||
private NodeContext nodeContext;
|
||||
|
||||
/**
|
||||
* Construct
|
||||
*
|
||||
* @param elementName
|
||||
* @param dictionary
|
||||
* @param importer
|
||||
*/
|
||||
public NodeItemContext(QName elementName, NodeContext nodeContext)
|
||||
{
|
||||
super(elementName, nodeContext.getDictionaryService(), nodeContext.getImporter());
|
||||
this.nodeContext = nodeContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Node Context
|
||||
*/
|
||||
public NodeContext getNodeContext()
|
||||
{
|
||||
return nodeContext;
|
||||
}
|
||||
}
|
131
source/java/org/alfresco/repo/importer/view/ParentContext.java
Normal file
131
source/java/org/alfresco/repo/importer/view/ParentContext.java
Normal file
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
* either express or implied. See the License for the specific
|
||||
* language governing permissions and limitations under the
|
||||
* License.
|
||||
*/
|
||||
package org.alfresco.repo.importer.view;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.alfresco.repo.importer.ImportParent;
|
||||
import org.alfresco.repo.importer.Importer;
|
||||
import org.alfresco.service.cmr.dictionary.AspectDefinition;
|
||||
import org.alfresco.service.cmr.dictionary.ChildAssociationDefinition;
|
||||
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
||||
import org.alfresco.service.cmr.dictionary.TypeDefinition;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.view.ImporterException;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
|
||||
|
||||
/**
|
||||
* Maintains state about the parent context of the node being imported.
|
||||
*
|
||||
* @author David Caruana
|
||||
*
|
||||
*/
|
||||
public class ParentContext extends ElementContext
|
||||
implements ImportParent
|
||||
{
|
||||
private NodeRef parentRef;
|
||||
private QName assocType;
|
||||
|
||||
|
||||
/**
|
||||
* Construct
|
||||
*
|
||||
* @param dictionary
|
||||
* @param configuration
|
||||
* @param progress
|
||||
* @param elementName
|
||||
* @param parentRef
|
||||
* @param assocType
|
||||
*/
|
||||
public ParentContext(QName elementName, DictionaryService dictionary, Importer importer)
|
||||
{
|
||||
super(elementName, dictionary, importer);
|
||||
parentRef = importer.getRootRef();
|
||||
assocType = importer.getRootAssocType();
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct (with unknown child association)
|
||||
*
|
||||
* @param elementName
|
||||
* @param parent
|
||||
*/
|
||||
public ParentContext(QName elementName, NodeContext parent)
|
||||
{
|
||||
super(elementName, parent.getDictionaryService(), parent.getImporter());
|
||||
parentRef = parent.getNodeRef();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Construct
|
||||
*
|
||||
* @param elementName
|
||||
* @param parent
|
||||
* @param childDef
|
||||
*/
|
||||
public ParentContext(QName elementName, NodeContext parent, ChildAssociationDefinition childDef)
|
||||
{
|
||||
this(elementName, parent);
|
||||
|
||||
// Ensure association is valid for node
|
||||
Set<QName> allAspects = new HashSet<QName>();
|
||||
for (AspectDefinition typeAspect : parent.getTypeDefinition().getDefaultAspects())
|
||||
{
|
||||
allAspects.add(typeAspect.getName());
|
||||
}
|
||||
allAspects.addAll(parent.getNodeAspects());
|
||||
TypeDefinition anonymousType = getDictionaryService().getAnonymousType(parent.getTypeDefinition().getName(), allAspects);
|
||||
Map<QName, ChildAssociationDefinition> nodeAssociations = anonymousType.getChildAssociations();
|
||||
if (nodeAssociations.containsKey(childDef.getName()) == false)
|
||||
{
|
||||
throw new ImporterException("Association " + childDef.getName() + " is not valid for node " + parent.getTypeDefinition().getName());
|
||||
}
|
||||
|
||||
parentRef = parent.getNodeRef();
|
||||
assocType = childDef.getName();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.alfresco.repo.importer.ImportParent#getParentRef()
|
||||
*/
|
||||
public NodeRef getParentRef()
|
||||
{
|
||||
return parentRef;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.alfresco.repo.importer.ImportParent#getAssocType()
|
||||
*/
|
||||
public QName getAssocType()
|
||||
{
|
||||
return assocType;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "ParentContext[parent=" + parentRef + ",assocType=" + getAssocType() + "]";
|
||||
}
|
||||
|
||||
}
|
634
source/java/org/alfresco/repo/importer/view/ViewParser.java
Normal file
634
source/java/org/alfresco/repo/importer/view/ViewParser.java
Normal file
@@ -0,0 +1,634 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
* either express or implied. See the License for the specific
|
||||
* language governing permissions and limitations under the
|
||||
* License.
|
||||
*/
|
||||
package org.alfresco.repo.importer.view;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.util.Stack;
|
||||
|
||||
import org.alfresco.repo.importer.Importer;
|
||||
import org.alfresco.repo.importer.Parser;
|
||||
import org.alfresco.service.cmr.dictionary.AspectDefinition;
|
||||
import org.alfresco.service.cmr.dictionary.ChildAssociationDefinition;
|
||||
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
||||
import org.alfresco.service.cmr.dictionary.PropertyDefinition;
|
||||
import org.alfresco.service.cmr.dictionary.TypeDefinition;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.view.ImporterException;
|
||||
import org.alfresco.service.namespace.NamespaceService;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
import org.xmlpull.v1.XmlPullParserFactory;
|
||||
|
||||
|
||||
/**
|
||||
* Importer for parsing and importing nodes given the Repository View schema.
|
||||
*
|
||||
* @author David Caruana
|
||||
*/
|
||||
public class ViewParser implements Parser
|
||||
{
|
||||
// Logger
|
||||
private static final Log logger = LogFactory.getLog(ViewParser.class);
|
||||
|
||||
// View schema elements and attributes
|
||||
private static final String VIEW_CHILD_NAME_ATTR = "childName";
|
||||
private static final String VIEW_DATATYPE_ATTR = "datatype";
|
||||
private static final String VIEW_ISNULL_ATTR = "isNull";
|
||||
private static final QName VIEW_METADATA = QName.createQName(NamespaceService.REPOSITORY_VIEW_1_0_URI, "metadata");
|
||||
private static final QName VIEW_VALUE_QNAME = QName.createQName(NamespaceService.REPOSITORY_VIEW_1_0_URI, "value");
|
||||
private static final QName VIEW_VALUES_QNAME = QName.createQName(NamespaceService.REPOSITORY_VIEW_1_0_URI, "values");
|
||||
private static final QName VIEW_ASPECTS = QName.createQName(NamespaceService.REPOSITORY_VIEW_1_0_URI, "aspects");
|
||||
private static final QName VIEW_PROPERTIES = QName.createQName(NamespaceService.REPOSITORY_VIEW_1_0_URI, "properties");
|
||||
private static final QName VIEW_ASSOCIATIONS = QName.createQName(NamespaceService.REPOSITORY_VIEW_1_0_URI, "associations");
|
||||
|
||||
// XML Pull Parser Factory
|
||||
private XmlPullParserFactory factory;
|
||||
|
||||
// Supporting services
|
||||
private NamespaceService namespaceService;
|
||||
private DictionaryService dictionaryService;
|
||||
|
||||
|
||||
/**
|
||||
* Construct
|
||||
*/
|
||||
public ViewParser()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Construct Xml Pull Parser Factory
|
||||
factory = XmlPullParserFactory.newInstance(System.getProperty(XmlPullParserFactory.PROPERTY_NAME), this.getClass());
|
||||
factory.setNamespaceAware(true);
|
||||
}
|
||||
catch (XmlPullParserException e)
|
||||
{
|
||||
throw new ImporterException("Failed to initialise view importer", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param namespaceService the namespace service
|
||||
*/
|
||||
public void setNamespaceService(NamespaceService namespaceService)
|
||||
{
|
||||
this.namespaceService = namespaceService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param dictionaryService the dictionary service
|
||||
*/
|
||||
public void setDictionaryService(DictionaryService dictionaryService)
|
||||
{
|
||||
this.dictionaryService = dictionaryService;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.alfresco.repo.importer.Parser#parse(java.io.Reader, org.alfresco.repo.importer.Importer)
|
||||
*/
|
||||
public void parse(Reader viewReader, Importer importer)
|
||||
{
|
||||
try
|
||||
{
|
||||
XmlPullParser xpp = factory.newPullParser();
|
||||
xpp.setInput(viewReader);
|
||||
Stack<ElementContext> contextStack = new Stack<ElementContext>();
|
||||
|
||||
try
|
||||
{
|
||||
for (int eventType = xpp.getEventType(); eventType != XmlPullParser.END_DOCUMENT; eventType = xpp.next())
|
||||
{
|
||||
switch (eventType)
|
||||
{
|
||||
case XmlPullParser.START_TAG:
|
||||
{
|
||||
if (xpp.getDepth() == 1)
|
||||
{
|
||||
processRoot(xpp, importer, contextStack);
|
||||
}
|
||||
else
|
||||
{
|
||||
processStartElement(xpp, contextStack);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case XmlPullParser.END_TAG:
|
||||
{
|
||||
processEndElement(xpp, contextStack);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
throw new ImporterException("Failed to import package at line " + xpp.getLineNumber() + "; column " + xpp.getColumnNumber() + " due to error: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
catch(XmlPullParserException e)
|
||||
{
|
||||
throw new ImporterException("Failed to parse view", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process start of xml element
|
||||
*
|
||||
* @param xpp
|
||||
* @param contextStack
|
||||
* @throws XmlPullParserException
|
||||
* @throws IOException
|
||||
*/
|
||||
private void processStartElement(XmlPullParser xpp, Stack<ElementContext> contextStack)
|
||||
throws XmlPullParserException, IOException
|
||||
{
|
||||
// Extract qualified name
|
||||
QName defName = getName(xpp);
|
||||
|
||||
// Process the element
|
||||
Object context = contextStack.peek();
|
||||
|
||||
// Handle special view directives
|
||||
if (defName.equals(VIEW_METADATA))
|
||||
{
|
||||
contextStack.push(new MetaDataContext(defName, (ElementContext)context));
|
||||
}
|
||||
else if (defName.equals(VIEW_ASPECTS) || defName.equals(VIEW_PROPERTIES) || defName.equals(VIEW_ASSOCIATIONS))
|
||||
{
|
||||
if (context instanceof NodeItemContext)
|
||||
{
|
||||
throw new ImporterException("Cannot nest element " + defName + " within " + ((NodeItemContext)context).getElementName());
|
||||
}
|
||||
if (!(context instanceof NodeContext))
|
||||
{
|
||||
throw new ImporterException("Element " + defName + " can only be declared within a node");
|
||||
}
|
||||
NodeContext nodeContext = (NodeContext)context;
|
||||
contextStack.push(new NodeItemContext(defName, nodeContext));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (context instanceof MetaDataContext)
|
||||
{
|
||||
processMetaData(xpp, defName, contextStack);
|
||||
}
|
||||
else if (context instanceof ParentContext)
|
||||
{
|
||||
// Process type definition
|
||||
TypeDefinition typeDef = dictionaryService.getType(defName);
|
||||
if (typeDef == null)
|
||||
{
|
||||
throw new ImporterException("Type " + defName + " has not been defined in the Repository dictionary");
|
||||
}
|
||||
processStartType(xpp, typeDef, contextStack);
|
||||
return;
|
||||
}
|
||||
else if (context instanceof NodeContext)
|
||||
{
|
||||
// Process children of node
|
||||
// Note: Process in the following order: aspects, properties and associations
|
||||
Object def = ((NodeContext)context).determineDefinition(defName);
|
||||
if (def == null)
|
||||
{
|
||||
throw new ImporterException("Definition " + defName + " is not valid; cannot find in Repository dictionary");
|
||||
}
|
||||
|
||||
if (def instanceof AspectDefinition)
|
||||
{
|
||||
processAspect(xpp, (AspectDefinition)def, contextStack);
|
||||
return;
|
||||
}
|
||||
else if (def instanceof PropertyDefinition)
|
||||
{
|
||||
processProperty(xpp, ((PropertyDefinition)def).getName(), contextStack);
|
||||
return;
|
||||
}
|
||||
else if (def instanceof ChildAssociationDefinition)
|
||||
{
|
||||
processStartChildAssoc(xpp, (ChildAssociationDefinition)def, contextStack);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: general association
|
||||
}
|
||||
}
|
||||
else if (context instanceof NodeItemContext)
|
||||
{
|
||||
NodeItemContext nodeItemContext = (NodeItemContext)context;
|
||||
NodeContext nodeContext = nodeItemContext.getNodeContext();
|
||||
QName itemName = nodeItemContext.getElementName();
|
||||
if (itemName.equals(VIEW_ASPECTS))
|
||||
{
|
||||
AspectDefinition def = nodeContext.determineAspect(defName);
|
||||
if (def == null)
|
||||
{
|
||||
throw new ImporterException("Aspect name " + defName + " is not valid; cannot find in Repository dictionary");
|
||||
}
|
||||
processAspect(xpp, def, contextStack);
|
||||
}
|
||||
else if (itemName.equals(VIEW_PROPERTIES))
|
||||
{
|
||||
// Note: Allow properties which do not have a data dictionary definition
|
||||
processProperty(xpp, defName, contextStack);
|
||||
}
|
||||
else if (itemName.equals(VIEW_ASSOCIATIONS))
|
||||
{
|
||||
// TODO: Handle general associations...
|
||||
ChildAssociationDefinition def = (ChildAssociationDefinition)nodeContext.determineAssociation(defName);
|
||||
if (def == null)
|
||||
{
|
||||
throw new ImporterException("Association name " + defName + " is not valid; cannot find in Repository dictionary");
|
||||
}
|
||||
processStartChildAssoc(xpp, def, contextStack);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process Root
|
||||
*
|
||||
* @param xpp
|
||||
* @param parentRef
|
||||
* @param childAssocType
|
||||
* @param configuration
|
||||
* @param progress
|
||||
* @param contextStack
|
||||
* @throws XmlPullParserException
|
||||
* @throws IOException
|
||||
*/
|
||||
private void processRoot(XmlPullParser xpp, Importer importer, Stack<ElementContext> contextStack)
|
||||
throws XmlPullParserException, IOException
|
||||
{
|
||||
ParentContext parentContext = new ParentContext(getName(xpp), dictionaryService, importer);
|
||||
contextStack.push(parentContext);
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug(indentLog("Pushed " + parentContext, contextStack.size() -1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Process meta-data
|
||||
*
|
||||
* @param xpp
|
||||
* @param metaDataName
|
||||
* @param contextStack
|
||||
* @throws XmlPullParserException
|
||||
* @throws IOException
|
||||
*/
|
||||
private void processMetaData(XmlPullParser xpp, QName metaDataName, Stack<ElementContext> contextStack)
|
||||
throws XmlPullParserException, IOException
|
||||
{
|
||||
MetaDataContext context = (MetaDataContext)contextStack.peek();
|
||||
|
||||
String value = null;
|
||||
|
||||
int eventType = xpp.next();
|
||||
if (eventType == XmlPullParser.TEXT)
|
||||
{
|
||||
// Extract value
|
||||
value = xpp.getText();
|
||||
eventType = xpp.next();
|
||||
}
|
||||
if (eventType != XmlPullParser.END_TAG)
|
||||
{
|
||||
throw new ImporterException("Meta data element " + metaDataName + " is missing end tag");
|
||||
}
|
||||
|
||||
context.setProperty(metaDataName, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process start of a node definition
|
||||
*
|
||||
* @param xpp
|
||||
* @param typeDef
|
||||
* @param contextStack
|
||||
* @throws XmlPullParserException
|
||||
* @throws IOException
|
||||
*/
|
||||
private void processStartType(XmlPullParser xpp, TypeDefinition typeDef, Stack<ElementContext> contextStack)
|
||||
throws XmlPullParserException, IOException
|
||||
{
|
||||
ParentContext parentContext = (ParentContext)contextStack.peek();
|
||||
NodeContext context = new NodeContext(typeDef.getName(), parentContext, typeDef);
|
||||
|
||||
// Extract child name if explicitly defined
|
||||
String childName = xpp.getAttributeValue(NamespaceService.REPOSITORY_VIEW_1_0_URI, VIEW_CHILD_NAME_ATTR);
|
||||
if (childName != null && childName.length() > 0)
|
||||
{
|
||||
context.setChildName(childName);
|
||||
}
|
||||
|
||||
contextStack.push(context);
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug(indentLog("Pushed " + context, contextStack.size() -1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Process aspect definition
|
||||
*
|
||||
* @param xpp
|
||||
* @param aspectDef
|
||||
* @param contextStack
|
||||
* @throws XmlPullParserException
|
||||
* @throws IOException
|
||||
*/
|
||||
private void processAspect(XmlPullParser xpp, AspectDefinition aspectDef, Stack<ElementContext> contextStack)
|
||||
throws XmlPullParserException, IOException
|
||||
{
|
||||
NodeContext context = peekNodeContext(contextStack);
|
||||
context.addAspect(aspectDef);
|
||||
|
||||
int eventType = xpp.next();
|
||||
if (eventType != XmlPullParser.END_TAG)
|
||||
{
|
||||
throw new ImporterException("Aspect " + aspectDef.getName() + " definition is not valid - it cannot contain any elements");
|
||||
}
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug(indentLog("Processed aspect " + aspectDef.getName(), contextStack.size()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Process property definition
|
||||
*
|
||||
* @param xpp
|
||||
* @param propDef
|
||||
* @param contextStack
|
||||
* @throws XmlPullParserException
|
||||
* @throws IOException
|
||||
*/
|
||||
private void processProperty(XmlPullParser xpp, QName propertyName, Stack<ElementContext> contextStack)
|
||||
throws XmlPullParserException, IOException
|
||||
{
|
||||
NodeContext context = peekNodeContext(contextStack);
|
||||
|
||||
// Extract single value
|
||||
String value = "";
|
||||
int eventType = xpp.next();
|
||||
if (eventType == XmlPullParser.TEXT)
|
||||
{
|
||||
value = xpp.getText();
|
||||
eventType = xpp.next();
|
||||
}
|
||||
if (eventType == XmlPullParser.END_TAG)
|
||||
{
|
||||
context.addProperty(propertyName, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
// Extract collection, if specified
|
||||
boolean isCollection = false;
|
||||
if (eventType == XmlPullParser.START_TAG)
|
||||
{
|
||||
QName name = getName(xpp);
|
||||
if (name.equals(VIEW_VALUES_QNAME))
|
||||
{
|
||||
context.addPropertyCollection(propertyName);
|
||||
isCollection = true;
|
||||
eventType = xpp.next();
|
||||
if (eventType == XmlPullParser.TEXT)
|
||||
{
|
||||
eventType = xpp.next();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Extract decorated value
|
||||
while (eventType == XmlPullParser.START_TAG)
|
||||
{
|
||||
QName name = getName(xpp);
|
||||
if (!name.equals(VIEW_VALUE_QNAME))
|
||||
{
|
||||
throw new ImporterException("Invalid view structure - expected element " + VIEW_VALUE_QNAME + " for property " + propertyName);
|
||||
}
|
||||
QName datatype = QName.createQName(xpp.getAttributeValue(NamespaceService.REPOSITORY_VIEW_1_0_URI, VIEW_DATATYPE_ATTR), namespaceService);
|
||||
Boolean isNull = Boolean.valueOf(xpp.getAttributeValue(NamespaceService.REPOSITORY_VIEW_1_0_URI, VIEW_ISNULL_ATTR));
|
||||
String decoratedValue = isNull ? null : "";
|
||||
eventType = xpp.next();
|
||||
if (eventType == XmlPullParser.TEXT)
|
||||
{
|
||||
decoratedValue = xpp.getText();
|
||||
eventType = xpp.next();
|
||||
}
|
||||
if (eventType == XmlPullParser.END_TAG)
|
||||
{
|
||||
context.addProperty(propertyName, decoratedValue);
|
||||
if (datatype != null)
|
||||
{
|
||||
context.addDatatype(propertyName, dictionaryService.getDataType(datatype));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ImporterException("Value for property " + propertyName + " has not been defined correctly - missing end tag");
|
||||
}
|
||||
eventType = xpp.next();
|
||||
if (eventType == XmlPullParser.TEXT)
|
||||
{
|
||||
eventType = xpp.next();
|
||||
}
|
||||
}
|
||||
|
||||
// End of value
|
||||
if (eventType != XmlPullParser.END_TAG)
|
||||
{
|
||||
throw new ImporterException("Invalid view structure - property " + propertyName + " definition is invalid");
|
||||
}
|
||||
|
||||
// End of collection
|
||||
if (isCollection)
|
||||
{
|
||||
eventType = xpp.next();
|
||||
if (eventType == XmlPullParser.TEXT)
|
||||
{
|
||||
eventType = xpp.next();
|
||||
}
|
||||
if (eventType != XmlPullParser.END_TAG)
|
||||
{
|
||||
throw new ImporterException("Invalid view structure - property " + propertyName + " definition is invalid");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug(indentLog("Processed property " + propertyName, contextStack.size()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Process start of child association definition
|
||||
*
|
||||
* @param xpp
|
||||
* @param childAssocDef
|
||||
* @param contextStack
|
||||
* @throws XmlPullParserException
|
||||
* @throws IOException
|
||||
*/
|
||||
private void processStartChildAssoc(XmlPullParser xpp, ChildAssociationDefinition childAssocDef, Stack<ElementContext> contextStack)
|
||||
throws XmlPullParserException, IOException
|
||||
{
|
||||
NodeContext context = peekNodeContext(contextStack);
|
||||
|
||||
if (context.getNodeRef() == null)
|
||||
{
|
||||
// Create Node
|
||||
NodeRef nodeRef = context.getImporter().importNode(context);
|
||||
context.setNodeRef(nodeRef);
|
||||
}
|
||||
|
||||
// Construct Child Association Context
|
||||
ParentContext parentContext = new ParentContext(childAssocDef.getName(), context, childAssocDef);
|
||||
contextStack.push(parentContext);
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug(indentLog("Pushed " + parentContext, contextStack.size() -1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Process end of xml element
|
||||
*
|
||||
* @param xpp
|
||||
* @param contextStack
|
||||
*/
|
||||
private void processEndElement(XmlPullParser xpp, Stack<ElementContext> contextStack)
|
||||
{
|
||||
ElementContext context = contextStack.peek();
|
||||
if (context.getElementName().getLocalName().equals(xpp.getName()) &&
|
||||
context.getElementName().getNamespaceURI().equals(xpp.getNamespace()))
|
||||
{
|
||||
context = contextStack.pop();
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug(indentLog("Popped " + context, contextStack.size()));
|
||||
|
||||
if (context instanceof NodeContext)
|
||||
{
|
||||
processEndType((NodeContext)context);
|
||||
}
|
||||
else if (context instanceof ParentContext)
|
||||
{
|
||||
processEndChildAssoc((ParentContext)context);
|
||||
}
|
||||
else if (context instanceof MetaDataContext)
|
||||
{
|
||||
processEndMetaData((MetaDataContext)context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process end of the type definition
|
||||
*
|
||||
* @param context
|
||||
*/
|
||||
private void processEndType(NodeContext context)
|
||||
{
|
||||
NodeRef nodeRef = context.getNodeRef();
|
||||
if (nodeRef == null)
|
||||
{
|
||||
nodeRef = context.getImporter().importNode(context);
|
||||
context.setNodeRef(nodeRef);
|
||||
}
|
||||
context.getImporter().childrenImported(nodeRef);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process end of the child association
|
||||
*
|
||||
* @param context
|
||||
*/
|
||||
private void processEndChildAssoc(ParentContext context)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Process end of meta data
|
||||
*
|
||||
* @param context
|
||||
*/
|
||||
private void processEndMetaData(MetaDataContext context)
|
||||
{
|
||||
context.getImporter().importMetaData(context.getProperties());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get parent Node Context
|
||||
*
|
||||
* @param contextStack context stack
|
||||
* @return node context
|
||||
*/
|
||||
private NodeContext peekNodeContext(Stack<ElementContext> contextStack)
|
||||
{
|
||||
ElementContext context = contextStack.peek();
|
||||
if (context instanceof NodeContext)
|
||||
{
|
||||
return (NodeContext)context;
|
||||
}
|
||||
else if (context instanceof NodeItemContext)
|
||||
{
|
||||
return ((NodeItemContext)context).getNodeContext();
|
||||
}
|
||||
throw new ImporterException("Internal error: Failed to retrieve node context");
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to create Qualified name from current xml element
|
||||
*
|
||||
* @param xpp
|
||||
* @return
|
||||
*/
|
||||
private QName getName(XmlPullParser xpp)
|
||||
{
|
||||
// Ensure namespace is valid
|
||||
String uri = xpp.getNamespace();
|
||||
if (namespaceService.getURIs().contains(uri) == false)
|
||||
{
|
||||
throw new ImporterException("Namespace URI " + uri + " has not been defined in the Repository dictionary");
|
||||
}
|
||||
|
||||
// Construct name
|
||||
String name = xpp.getName();
|
||||
return QName.createQName(uri, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to indent debug output
|
||||
*
|
||||
* @param msg
|
||||
* @param depth
|
||||
* @return
|
||||
*/
|
||||
private String indentLog(String msg, int depth)
|
||||
{
|
||||
StringBuffer buf = new StringBuffer(1024);
|
||||
for (int i = 0; i < depth; i++)
|
||||
{
|
||||
buf.append(' ');
|
||||
}
|
||||
buf.append(msg);
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user