/*
* Copyright (C) 2005-2009 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package fr.openwide.talendalfresco.alfresco.importer;
import java.io.IOException;
import java.io.Reader;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
import org.alfresco.repo.importer.Importer;
import org.alfresco.repo.importer.Parser;
import org.alfresco.repo.importer.view.ElementContext;
import org.alfresco.repo.importer.view.MetaDataContext;
import org.alfresco.repo.importer.view.NodeContext;
import org.alfresco.repo.importer.view.NodeItemContext;
import org.alfresco.repo.importer.view.ParentContext;
import org.alfresco.repo.importer.view.ViewParser;
import org.alfresco.service.cmr.dictionary.AspectDefinition;
import org.alfresco.service.cmr.dictionary.AssociationDefinition;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
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.MLText;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter;
import org.alfresco.service.cmr.security.AccessStatus;
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;
/**
* [talendalfresco] "almost" copy of ViewParser v3.2 to allow extending it.
* Changes : changed private to protected to allow override for :
* startReference(), to allow catching non existing asso refs errors
* processEndType(), to allow for a separate transactions for rules & behaviours
* importNode(), to refer to it in overriden sprocessEndType()
* logger, nodeService
* ParserContext
*
* Importer for parsing and importing nodes given the Repository View schema.
*
* @author David Caruana
*/
public class ViewParserBase implements Parser
{
// Logger
protected 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 String VIEW_INHERIT_PERMISSIONS_ATTR = "inherit";
private static final String VIEW_ACCESS_STATUS_ATTR = "access";
private static final String VIEW_ID_ATTR = "id";
private static final String VIEW_IDREF_ATTR = "idref";
private static final String VIEW_PATHREF_ATTR = "pathref";
private static final String VIEW_NODEREF_ATTR = "noderef";
private static final String VIEW_LOCALE_ATTR = "locale";
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");
private static final QName VIEW_ACL = QName.createQName(NamespaceService.REPOSITORY_VIEW_1_0_URI, "acl");
private static final QName VIEW_ACE = QName.createQName(NamespaceService.REPOSITORY_VIEW_1_0_URI, "ace");
private static final QName VIEW_AUTHORITY = QName.createQName(NamespaceService.REPOSITORY_VIEW_1_0_URI, "authority");
private static final QName VIEW_PERMISSION = QName.createQName(NamespaceService.REPOSITORY_VIEW_1_0_URI, "permission");
private static final QName VIEW_REFERENCE = QName.createQName(NamespaceService.REPOSITORY_VIEW_1_0_URI, "reference");
private static final QName VIEW_ML_VALUE = QName.createQName(NamespaceService.REPOSITORY_VIEW_1_0_URI, "mlvalue");
// XML Pull Parser Factory
private XmlPullParserFactory factory;
// Supporting services
private NamespaceService namespaceService;
protected NodeService nodeService;
private DictionaryService dictionaryService;
// Parser Context maintained during each parse
protected class ParserContext
{
Importer importer;
DictionaryService dictionaryService;
Stack<ElementContext> elementStack;
Map<String, NodeRef> importIds = new HashMap<String, NodeRef>();
}
/**
* Construct
*/
public ViewParserBase()
{
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 nodeService the node service
*/
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
/**
* @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);
ParserContext parserContext = new ParserContext();
parserContext.importer = importer;
parserContext.dictionaryService = dictionaryService;
parserContext.elementStack = 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, parserContext);
}
else
{
processStartElement(xpp, parserContext);
}
break;
}
case XmlPullParser.END_TAG:
{
processEndElement(xpp, parserContext);
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, ParserContext parserContext)
throws XmlPullParserException, IOException
{
// Extract qualified name
QName defName = getName(xpp);
// Process the element
Object element = parserContext.elementStack.peek();
// Handle special view directives
if (defName.equals(VIEW_METADATA))
{
MetaDataContext metaDataContext = new MetaDataContext(defName, (ElementContext)element);
parserContext.elementStack.push(metaDataContext);
if (logger.isDebugEnabled())
logger.debug(indentLog("Pushed " + metaDataContext, parserContext.elementStack.size() -1));
}
else if (defName.equals(VIEW_ASPECTS) || defName.equals(VIEW_PROPERTIES) || defName.equals(VIEW_ASSOCIATIONS) || defName.equals(VIEW_ACL))
{
if (element instanceof NodeItemContext)
{
throw new ImporterException("Cannot nest element " + defName + " within " + ((NodeItemContext)element).getElementName());
}
if (!(element instanceof NodeContext))
{
throw new ImporterException("Element " + defName + " can only be declared within a node");
}
NodeContext node = (NodeContext)element;
NodeItemContext nodeItemContext = new NodeItemContext(defName, node);
parserContext.elementStack.push(nodeItemContext);
if (logger.isDebugEnabled())
logger.debug(indentLog("Pushed " + nodeItemContext, parserContext.elementStack.size() -1));
// process ACL specific attributes
if (defName.equals(VIEW_ACL))
{
processACL(xpp, parserContext);
}
}
else
{
if (element instanceof MetaDataContext)
{
processMetaData(xpp, defName, parserContext);
}
else if (element instanceof ParentContext)
{
if (defName.equals(VIEW_REFERENCE))
{
// Process reference
processStartReference(xpp, defName, parserContext);
}
else
{
// 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, parserContext);
}
return;
}
else if (element instanceof NodeContext)
{
// Process children of node
// Note: Process in the following order: properties aspects, and associations
Object def = determineDefinition(((NodeContext)element), 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, parserContext);
return;
}
else if (def instanceof PropertyDefinition)
{
processProperty(xpp, ((PropertyDefinition)def).getName(), parserContext);
return;
}
else if (def instanceof AssociationDefinition)
{
processStartAssoc(xpp, (AssociationDefinition)def, parserContext);
return;
}
}
else if (element instanceof NodeItemContext)
{
NodeItemContext nodeItem = (NodeItemContext)element;
NodeContext node = nodeItem.getNodeContext();
QName itemName = nodeItem.getElementName();
if (itemName.equals(VIEW_ASPECTS))
{
AspectDefinition def = node.determineAspect(defName);
if (def == null)
{
throw new ImporterException("Aspect name " + defName + " is not valid; cannot find in Repository dictionary");
}
processAspect(xpp, def, parserContext);
}
else if (itemName.equals(VIEW_PROPERTIES))
{
// Note: Allow properties which do not have a data dictionary definition
processProperty(xpp, defName, parserContext);
}
else if (itemName.equals(VIEW_ASSOCIATIONS))
{
AssociationDefinition def = (AssociationDefinition)node.determineAssociation(defName);
if (def == null)
{
throw new ImporterException("Association name " + defName + " is not valid; cannot find in Repository dictionary");
}
processStartAssoc(xpp, (AssociationDefinition)def, parserContext);
}
else if (itemName.equals(VIEW_ACL))
{
processAccessControlEntry(xpp, parserContext);
}
}
}
}
public Object determineDefinition(NodeContext ctx, QName defName)
{
Object def = ctx.determineProperty(defName);
if (def == null)
{
def = ctx.determineAspect(defName);
if (def == null)
{
def = ctx.determineAssociation(defName);
}
}
return def;
}
/**
* Process Root
*
* @param xpp
* @param parentRef
* @param childAssocType
* @param configuration
* @param progress
* @param contextStack
* @throws XmlPullParserException
* @throws IOException
*/
private void processRoot(XmlPullParser xpp, ParserContext parserContext)
throws XmlPullParserException, IOException
{
ParentContext parent = new ParentContext(getName(xpp), parserContext.dictionaryService, parserContext.importer);
parserContext.elementStack.push(parent);
if (logger.isDebugEnabled())
logger.debug(indentLog("Pushed " + parent, parserContext.elementStack.size() -1));
}
/**
* Process meta-data
*
* @param xpp
* @param metaDataName
* @param contextStack
* @throws XmlPullParserException
* @throws IOException
*/
private void processMetaData(XmlPullParser xpp, QName metaDataName, ParserContext parserContext)
throws XmlPullParserException, IOException
{
MetaDataContext metaData = (MetaDataContext)parserContext.elementStack.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");
}
metaData.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, ParserContext parserContext)
throws XmlPullParserException, IOException
{
ParentContext parent = (ParentContext)parserContext.elementStack.peek();
NodeContext node = new NodeContext(typeDef.getName(), parent, 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)
{
node.setChildName(childName);
}
// Extract import id if explicitly defined
String importId = xpp.getAttributeValue(NamespaceService.REPOSITORY_VIEW_1_0_URI, VIEW_ID_ATTR);
if (importId != null && importId.length() > 0)
{
node.setImportId(importId);
}
parserContext.elementStack.push(node);
if (logger.isDebugEnabled())
logger.debug(indentLog("Pushed " + node, parserContext.elementStack.size() -1));
}
/**
* Process start reference
*
* @param xpp
* @param typeDef
* @param contextStack
* @throws XmlPullParserException
* @throws IOException
*/
protected void processStartReference(XmlPullParser xpp, QName refName, ParserContext parserContext)
throws XmlPullParserException, IOException
{
ParentContext parent = (ParentContext)parserContext.elementStack.peek();
NodeContext node = new NodeContext(refName, parent, null);
node.setReference(true);
// Extract Import scoped reference Id if explicitly defined
String idRefAttr = xpp.getAttributeValue(NamespaceService.REPOSITORY_VIEW_1_0_URI, VIEW_IDREF_ATTR);
String pathRefAttr = xpp.getAttributeValue(NamespaceService.REPOSITORY_VIEW_1_0_URI, VIEW_PATHREF_ATTR);
String nodeRefAttr = xpp.getAttributeValue(NamespaceService.REPOSITORY_VIEW_1_0_URI, VIEW_NODEREF_ATTR);
if ((idRefAttr != null && idRefAttr.length() > 0) && (pathRefAttr != null && pathRefAttr.length() > 0) && (nodeRefAttr != null && nodeRefAttr.length() > 0))
{
// Do not support both IDREF and PATHREF
throw new ImporterException("Only one of " + VIEW_IDREF_ATTR + " or " + VIEW_PATHREF_ATTR + " or " + VIEW_NODEREF_ATTR + " can be specified.");
}
// Convert to Node Reference
NodeRef nodeRef = null;
if (nodeRefAttr != null)
{
nodeRef = new NodeRef(nodeRefAttr);
}
else if (idRefAttr != null && idRefAttr.length() > 0)
{
// retrieve uuid from previously imported node
nodeRef = getImportReference(parserContext, idRefAttr);
if (nodeRef == null)
{
throw new ImporterException("Cannot find node referenced by id " + idRefAttr);
}
}
else if (pathRefAttr != null && pathRefAttr.length() > 0)
{
nodeRef = parserContext.importer.resolvePath(pathRefAttr);
if (nodeRef == null)
{
throw new ImporterException("Cannot find node referenced by path " + pathRefAttr);
}
}
// Establish node definition
node.setUUID(nodeRef.getId());
node.setTypeDefinition(dictionaryService.getType(nodeService.getType(nodeRef)));
Set<QName> aspects = nodeService.getAspects(nodeRef);
for (QName aspect : aspects)
{
node.addAspect(dictionaryService.getAspect(aspect));
}
// 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)
{
node.setChildName(childName);
}
// Extract import id if explicitly defined
String importId = xpp.getAttributeValue(NamespaceService.REPOSITORY_VIEW_1_0_URI, VIEW_ID_ATTR);
if (importId != null && importId.length() > 0)
{
node.setImportId(importId);
}
parserContext.elementStack.push(node);
if (logger.isDebugEnabled())
logger.debug(indentLog("Pushed Reference " + node, parserContext.elementStack.size() -1));
}
/**
* Process aspect definition
*
* @param xpp
* @param aspectDef
* @param contextStack
* @throws XmlPullParserException
* @throws IOException
*/
private void processAspect(XmlPullParser xpp, AspectDefinition aspectDef, ParserContext parserContext)
throws XmlPullParserException, IOException
{
NodeContext node = peekNodeContext(parserContext.elementStack);
node.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(), parserContext.elementStack.size()));
}
/**
* Process ACL definition
*
* @param xpp
* @param contextStack
*/
private void processACL(XmlPullParser xpp, ParserContext parserContext)
{
NodeContext node = peekNodeContext(parserContext.elementStack);
String strInherit = xpp.getAttributeValue(NamespaceService.REPOSITORY_VIEW_1_0_URI, VIEW_INHERIT_PERMISSIONS_ATTR);
if (strInherit != null)
{
Boolean inherit = Boolean.valueOf(strInherit);
if (!inherit)
{
node.setInheritPermissions(false);
}
}
}
/**
* Process ACE definition
*
* @param xpp
* @param contextStack
* @throws XmlPullParserException
* @throws IOException
*/
private void processAccessControlEntry(XmlPullParser xpp, ParserContext parserContext)
throws XmlPullParserException, IOException
{
NodeContext node = peekNodeContext(parserContext.elementStack);
QName defName = getName(xpp);
if (!defName.equals(VIEW_ACE))
{
throw new ImporterException("Expected start element " + VIEW_ACE);
}
// extract Access Status
String access = xpp.getAttributeValue(NamespaceService.REPOSITORY_VIEW_1_0_URI, VIEW_ACCESS_STATUS_ATTR);
AccessStatus accessStatus = (access == null) ? AccessStatus.ALLOWED : AccessStatus.valueOf(AccessStatus.class, access);
if (accessStatus == null)
{
throw new ImporterException("Permission access status '" + access + "' is not recognised.");
}
// extract authority and permission
String authority = null;
String permission = null;
int eventType = xpp.next();
while (eventType != XmlPullParser.END_TAG)
{
if (eventType == XmlPullParser.START_TAG)
{
defName = getName(xpp);
if (defName.equals(VIEW_AUTHORITY))
{
eventType = xpp.next();
if (eventType != XmlPullParser.TEXT)
{
throw new ImporterException("Element " + VIEW_AUTHORITY + " must have a value");
}
authority = xpp.getText();
}
else if (defName.equals(VIEW_PERMISSION))
{
eventType = xpp.next();
if (eventType != XmlPullParser.TEXT)
{
throw new ImporterException("Element " + VIEW_PERMISSION + " must have a value");
}
permission = xpp.getText();
}
else
{
throw new ImporterException("Expected start element " + VIEW_AUTHORITY + " or " + VIEW_PERMISSION);
}
eventType = xpp.next();
if (eventType != XmlPullParser.END_TAG)
{
throw new ImporterException("Expected end element " + defName);
}
QName endDefName = getName(xpp);
if (!defName.equals(endDefName))
{
throw new ImporterException("Expected end element " + defName);
}
}
eventType = xpp.next();
}
// validate authority and permission
if (authority == null || authority.length() == 0)
{
throw new ImporterException("Authority must be specified");
}
if (permission == null || permission.length() == 0)
{
throw new ImporterException("Permisssion must be specified");
}
// extract end of ace
defName = getName(xpp);
if (!defName.equals(VIEW_ACE))
{
throw new ImporterException("Expected end element " + VIEW_ACE);
}
// update node context
node.addAccessControlEntry(accessStatus, authority, permission);
}
/**
* Process property definition
*
* @param xpp
* @param propDef
* @param contextStack
* @throws XmlPullParserException
* @throws IOException
*/
private void processProperty(XmlPullParser xpp, QName propertyName, ParserContext parserContext)
throws XmlPullParserException, IOException
{
NodeContext node = peekNodeContext(parserContext.elementStack);
// Extract single value
String value = "";
int eventType = xpp.next();
if (eventType == XmlPullParser.TEXT)
{
value = xpp.getText();
eventType = xpp.next();
}
if (eventType == XmlPullParser.END_TAG)
{
node.addProperty(propertyName, value);
}
else
{
// Extract collection, if specified
boolean isCollection = false;
boolean isMLProperty = false;
if (eventType == XmlPullParser.START_TAG)
{
QName name = getName(xpp);
if (name.equals(VIEW_VALUES_QNAME))
{
node.addPropertyCollection(propertyName);
isCollection = true;
eventType = xpp.next();
if (eventType == XmlPullParser.TEXT)
{
eventType = xpp.next();
}
}
else if (name.equals(VIEW_ML_VALUE))
{
isMLProperty = true;
}
}
// Extract ML value
if (isMLProperty)
{
if (logger.isDebugEnabled())
{
logger.debug("Start parsing MLValue for property: " + propertyName);
}
value = "";
String locale = "";
node.addDatatype(propertyName, dictionaryService.getDataType(DataTypeDefinition.MLTEXT));
MLText mlText = new MLText();
while (isMLProperty)
{
isMLProperty = false;
locale = xpp.getAttributeValue(NamespaceService.REPOSITORY_VIEW_1_0_URI, VIEW_LOCALE_ATTR);
eventType = xpp.next();
if (eventType == XmlPullParser.TEXT)
{
value = xpp.getText();
eventType = xpp.next();
}
if (eventType == XmlPullParser.END_TAG)
{
if (logger.isDebugEnabled())
{
logger.debug("Found ML entry: " + locale + "=" + value);
}
mlText.addValue(DefaultTypeConverter.INSTANCE.convert(Locale.class, locale), value);
eventType = xpp.next();
if (eventType == XmlPullParser.TEXT)
{
eventType = xpp.next();
}
}
if (eventType == XmlPullParser.START_TAG)
{
QName name = getName(xpp);
if (name.equals(VIEW_ML_VALUE))
{
isMLProperty = true;
}
}
}
if (logger.isDebugEnabled())
{
logger.debug("End parsing MLValue for property: " + propertyName);
}
node.addProperty(propertyName, mlText);
}
// 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)
{
node.addProperty(propertyName, decoratedValue);
if (datatype != null)
{
node.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, parserContext.elementStack.size()));
}
/**
* Process start of association definition
*
* @param xpp
* @param AssocDef
* @param contextStack
* @throws XmlPullParserException
* @throws IOException
*/
private void processStartAssoc(XmlPullParser xpp, AssociationDefinition assocDef, ParserContext parserContext)
throws XmlPullParserException, IOException
{
NodeContext node = peekNodeContext(parserContext.elementStack);
importNode(parserContext, node);
// Construct Child Association Context
ParentContext parent = new ParentContext(assocDef.getName(), node, assocDef);
parserContext.elementStack.push(parent);
if (logger.isDebugEnabled())
logger.debug(indentLog("Pushed " + parent, parserContext.elementStack.size() -1));
}
/**
* Process end of xml element
*
* @param xpp
* @param contextStack
*/
private void processEndElement(XmlPullParser xpp, ParserContext parserContext)
{
ElementContext element = parserContext.elementStack.peek();
if (element.getElementName().getLocalName().equals(xpp.getName()) &&
element.getElementName().getNamespaceURI().equals(xpp.getNamespace()))
{
element = parserContext.elementStack.pop();
if (logger.isDebugEnabled())
logger.debug(indentLog("Popped " + element, parserContext.elementStack.size()));
if (element instanceof NodeContext)
{
processEndType(parserContext, (NodeContext)element);
}
else if (element instanceof ParentContext)
{
processEndAssoc(parserContext, (ParentContext)element);
}
else if (element instanceof MetaDataContext)
{
processEndMetaData(parserContext, (MetaDataContext)element);
}
}
}
/**
* Process end of the type definition
*
* @param node
*/
protected void processEndType(ParserContext parserContext, NodeContext node)
{
importNode(parserContext, node);
NodeRef nodeRef = node.getNodeRef();
node.getImporter().childrenImported(nodeRef);
}
/**
* Process end of the child association
*
* @param context
*/
private void processEndAssoc(ParserContext parserContext, ParentContext parent)
{
}
/**
* Process end of meta data
*
* @param context
*/
private void processEndMetaData(ParserContext parserContext, MetaDataContext context)
{
context.getImporter().importMetaData(context.getProperties());
}
/**
* Import node
*
* @param parserContext parser context
* @param node node context
*/
protected void importNode(ParserContext parserContext, NodeContext node)
{
if (node.getNodeRef() == null)
{
// Import Node
NodeRef nodeRef = node.getImporter().importNode(node);
node.setNodeRef(nodeRef);
// Maintain running list of "import" scoped ids
String importId = node.getImportId();
if (importId != null && importId.length() > 0)
{
createImportReference(parserContext, importId, nodeRef);
}
}
}
/**
* Maps an Import Id to a Node Reference
*
* @param importId import Id
* @param nodeRef node reference
*/
private void createImportReference(ParserContext parserContext, String importId, NodeRef nodeRef)
{
if (parserContext.importIds.containsKey(importId))
{
throw new ImporterException("Import id " + importId + " already specified within import file");
}
parserContext.importIds.put(importId, nodeRef);
}
/**
* Gets the Node Reference for the specified Import Id
*
* @param importId the import id
* @return the node reference
*/
private NodeRef getImportReference(ParserContext parserContext, String importId)
{
return parserContext.importIds.get(importId);
}
/**
* Get parent Node Context
*
* @param contextStack context stack
* @return node context
*/
private NodeContext peekNodeContext(Stack<ElementContext> contextStack)
{
ElementContext element = contextStack.peek();
if (element instanceof NodeContext)
{
return (NodeContext)element;
}
else if (element instanceof NodeItemContext)
{
return ((NodeItemContext)element).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();
}
}