/*******************************************************************************
$Source: /cvs/repositories/openii3/project/java/source/org/openeai/layouts/XmlLayout.java,v $
$Revision: 1.19 $
*******************************************************************************/
/**********************************************************************
This file is part of the OpenEAI Application Foundation or
OpenEAI Message Object API created by Tod Jackson
(tod@openeai.org) and Steve Wheat (steve@openeai.org) at
the University of Illinois Urbana-Champaign.
Copyright (C) 2002 The OpenEAI Software Foundation
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
For specific licensing details and examples of how this software
can be used to build commercial integration software or to implement
integrations for your enterprise, visit http://www.OpenEai.org/licensing.
*/
package org.openeai.layouts;
import java.util.*;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.Attribute;
import org.jdom.output.XMLOutputter;
import gnu.regexp.*;
import java.lang.reflect.Method;
import org.openeai.moa.*;
import org.openeai.config.EnterpriseFields;
import org.openeai.config.EnterpriseFieldException;
/**
* This is the default Layout manager that all Enterprise Messaging Objects use to
* build themselves from and serialize themselves to XML. It uses information found
* in the EnterpriseObjects documents to determine the layout corresponding to a particular
* object.
* <P>
* @author Tod Jackson (tod@openeai.org)
* @author Steve Wheat (steve@openeai.org)
* @version 3.0 - 28 January 2003
*/
public class XmlLayout extends EnterpriseLayoutManagerImpl
implements EnterpriseLayoutManager {
private Element m_layout = null;
private boolean m_validate = false;
/**
* Constructor
*/
public XmlLayout() {
}
public void init(String layoutManagerName, Document layoutDoc)
throws EnterpriseLayoutException {
// Default behavior is all that's needed for XmlLayout
super.init(layoutManagerName, layoutDoc);
}
/**
* This is the buildOutputFromObject method that all Layout Managers must implement.
* This particular layout manager returns an Element when this method is called.
* It is up to the caller of this method to cast the object to an Element. This Element
* is an XML Element that contains all the data currently residing in the object.
* The fields that data is retrieved from to build the element are defined in
* EnterpriseObjects document associated to the object being serialized.
* <P>
* All XmlEnterpriseObjects have a method that determines the layout manager being
* used and then calls this method on the currently used layout manager (this class for
* example). The method in XmlEnterpriseObject is also called buildOutputFromObject.
* <P>
* @param xeo XmlEnterpriseObject the object that's being used to build the element.
* @return Object the XML element that corresponds to the data in the object.
* @throws EnterpriseLayoutException if any errors occur getting the field values from the object.
*/
public Object buildOutputFromObject(XmlEnterpriseObject xeo)
throws EnterpriseLayoutException {
logger.debug("XmlLayout - Build Output From Object - Started Processing.");
String className = xeo.getClass().getName();
String objectName = className.substring(className.lastIndexOf('.') + 1); // Our based element in the XML passed in
if (xeo.isDate()) {
try {
Method getter = xeo.getClass().getMethod("getType", new Class[] {});
objectName = (String)getter.invoke(xeo, new Object[] {});
}
catch (Exception e) {
String errMessage = "Couldn't invoke method: getType() on object " +
xeo.getClass().getName() + " Exception: " + e.getMessage();
throw new EnterpriseLayoutException(errMessage, e);
}
}
logger.debug("[xmllayout buildOutputFromObject] ObjectName is " + objectName);
Element eOutput = new Element(objectName);
Element eLayout = getLayout(getLayoutRoot(), objectName);
String layoutName = eLayout.getAttribute("name").getValue();
logger.debug("eLayout is " + layoutName);
java.util.List lFields = eLayout.getChildren("Field");
logger.debug("There are " + lFields.size() + " Fields in the " + layoutName + " Layout.");
XMLOutputter xmlOut = new XMLOutputter();
for (int i=0; i<lFields.size(); i++) {
Element eField = (Element)lFields.get(i);
buildElement(xeo, eOutput, eField, true);
logger.debug("eOutput is now " + xmlOut.outputString(eOutput));
}
logger.debug("XmlLayout - Build Output From Object - Ended Processing.");
return eOutput;
}
/**
* This is the buildOutputFromObject method that all Layout Managers must implement.
* This particular layout manager returns an Element with application specifice values when this method is called.
* It is up to the caller of this method to cast the object to an Element. This Element
* is an XML Element that contains all the data currently residing in the object.
* The fields that data is retrieved from to build the element are defined in
* the EnterpriseObjects document associated to the object being serialized.
* <P>
* This method takes the contents currently stored in the object and builds an XML Element
* from that content which contains application specific values. These "translations" are
* performed based on information found in EnterpriseObjects.xml.
* <P>
* All XmlEnterpriseObjects have a method that determines the layout manager being
* used and then calls this method on the currently used layout manager (this class for
* example). The method in XmlEnterpriseObject is also called buildOutputFromObject.
* <P>
* @param anXmlEnterpriseObject XmlEnterpriseObject the object that's being used to build the element.
* @param appName String target application that Enterprise Values should be converted to.
* @return Object the XML element that corresponds to the data in the object.
* @throws EnterpriseLayoutException if any errors occur getting the field values from the object.
*/
public Object buildOutputFromObject(XmlEnterpriseObject anXmlEnterpriseObject, String appName)
throws EnterpriseLayoutException {
logger.debug("XmlLayout - Build Object From Input with AppName - Started Processing.");
setTargetAppName(appName);
Object obj = buildOutputFromObject(anXmlEnterpriseObject);
logger.debug("XmlLayout - Build Object From Input with AppName - Ended Processing.");
return obj;
}
/**
* This is the buildObjectFromInput method that all Layout Managers must implement.
* This particular layout manager builds the object passed in from an Element.
* This Element is an XML Element that contains all the data the caller wishes to
* populate the object with. This data can be application specific data and
* rules found in the EnterpriseOjbects document associated to the object being populated are then applied
* to the data in the XML Element and when it's all complete, the object contains
* Enterprise Values.
* <P>
* All XmlEnterpriseObjects have a method that determines the "input" layout manager being
* used and then calls this method on the currently used layout manager (this class for
* example). The method in XmlEnterpriseObject is also called buildObjectFromInput.
* <P>
* @param input Object the input data that is used to populate the object (an XML Element in this case).
* @param xeo XmlEnterpriseObject the object that's being populated from the Element passed in.
* @throws EnterpriseLayoutException if an errors occur setting the Object fields with data
* contained in the XML element.
*/
public void buildObjectFromInput(Object input, XmlEnterpriseObject xeo)
throws EnterpriseLayoutException {
logger.debug("XmlLayout - Build Object From Input - Started Processing.");
Element eInput = (Element)input; // Element from an XML document passed in.
String className = xeo.getClass().getName();
String objectName = className.substring(className.lastIndexOf('.') + 1); // Our based element in the XML passed in
logger.debug("ObjectName is " + objectName);
Element eLayout = getLayout(getLayoutRoot(), objectName);
String layoutName = eLayout.getAttribute("name").getValue();
logger.debug("eLayout is " + layoutName);
java.util.List lFields = eLayout.getChildren("Field");
logger.debug("There are " + lFields.size() + " Fields in the " + layoutName + " Layout.");
for (int i=0; i<lFields.size(); i++) {
Element eField = (Element)lFields.get(i);
buildObject(xeo, eInput, eField, true);
}
logger.debug("XmlLayout - Build Object From Input - Ended Processing.");
}
private void buildElement(XmlEnterpriseObject xeo, Element eOutput,
Element eField, boolean parentIsRequired)
throws EnterpriseLayoutException {
String className = xeo.getClass().getName();
String objectName = className.substring(className.lastIndexOf('.') + 1); // Our based element in the XML passed in
String elementName = eOutput.getName();
logger.debug("[buildElement] Name of Object passed in is " + objectName);
String fieldName = eField.getAttribute("name").getValue();
logger.debug("[buildElement] Field Name is " + fieldName);
String fieldType = eField.getAttribute("type").getValue();
logger.debug("Field Type is " + fieldType);
String fieldValue = "";
// Required has to be pulled from the Format element which should exist for
// all fields, even Fields of type "Object".
boolean isRequired = isRequired(eField);
if (fieldType.equals("Element")) {
if (isRepeating(xeo, fieldName)) {
int numObjects = getLength(xeo, fieldName);
for (int i=0; i<numObjects; i++) {
Integer iParm = new Integer(i);
Object obj = null;
if (getTargetAppName() == null) {
obj = getValueFromObject(xeo, fieldName, new Object[] {iParm}, new Class[] {Integer.TYPE});
}
else {
obj = getValueFromObject(xeo, getTargetAppName(), fieldName, new Object[] {iParm}, new Class[] {Integer.TYPE});
}
if (obj instanceof String) {
fieldValue = (String)obj;
Element eChildOutput = new Element(fieldName);
// Have to do this to validate the data since the object we're dealing
// with may not have had an EnterpriseFields object when it was populated.
try {
String tempValue = removeXmlEscapes(fieldValue);
// 4/4/2002 fix
// in this case, we really do want to set the value in the vector at the index
// so we don't keep adding items to it
setVariableValue(xeo, fieldName, tempValue , String.class, i);
}
catch (Exception e) {
throw new EnterpriseLayoutException("Error setting " + objectName + "/" + fieldName +
" to the value " + fieldValue + " Exception: " + e.getMessage(), e);
}
// Now, we have to get the value from the object again since it may have been
// modified when the setter methods are called above. Otherwise, we'll be
// creating the XML using the "non enterprise" value.
if (getTargetAppName() == null) {
fieldValue = (String)getValueFromObject(xeo, fieldName, new Object[] {iParm}, new Class[] {Integer.TYPE});
}
else {
fieldValue = (String)getValueFromObject(xeo, getTargetAppName(), fieldName, new Object[] {iParm}, new Class[] {Integer.TYPE});
}
eChildOutput.setText(fieldValue);
eOutput.addContent(eChildOutput);
}
else {
// should never get here.
}
}
if (numObjects == 0) {
// Have to check the parent field (if one exists) for situations
// where the Parent field isn't required but children of that Parent
// are. Example is DeceasedDate in BasicPerson. DeceasedDate is optional
// but Month,Day,Year are required if DeceasedDate is present...
if (isRequired && parentIsRequired && xeo.getEnterpriseFields().ignoreValidation() == false) {
throw new EnterpriseLayoutException("The " + fieldType + " " +
elementName + "/" + fieldName + " is required but it has no value. NumObjects: " + numObjects);
}
else {
logger.debug(fieldName + " on " + elementName + " is required but " + elementName + " isn't so we can ignore this missing field.");
}
}
}
else {
Object obj = null;
if (getTargetAppName() == null) {
obj = getValueFromObject(xeo, fieldName);
}
else {
obj = getValueFromObject(xeo, getTargetAppName(), fieldName);
}
if (obj instanceof String) {
fieldValue = (String)obj;
}
else {
}
if (fieldValue == null || fieldValue.trim().length() == 0) {
// Have to check the parent field (if one exists) for situations
// where the Parent field isn't required but children of that Parent
// are. Example is DeceasedDate in BasicPerson. DeceasedDate is optional
// but Month,Day,Year are required if DeceasedDate is present...
if (isRequired && parentIsRequired && xeo.getEnterpriseFields().ignoreValidation() == false) {
throw new EnterpriseLayoutException("The " + fieldType + " " +
elementName + "/" + fieldName + " is required but it has no value.");
}
else {
logger.debug(fieldName + " on " + elementName + " is required but " + elementName + " isn't so we can ignore this missing field.");
// Don't even add the Element since it's not required and there is no
// data in the object's field.
return;
}
}
// Have to do this to validate the data since the object we're dealing
// with may not have had an EnterpriseFields object when it was populated.
try {
String tempValue = removeXmlEscapes(fieldValue);
setVariableValue(xeo, fieldName, tempValue , String.class);
}
catch (Exception e) {
throw new EnterpriseLayoutException("Error setting " + objectName + "/" + fieldName +
" to the value " + fieldValue + " Exception: " + e.getMessage(), e);
}
// Now, we have to get the value from the object again since it may have been
// modified when the setter methods are called above. Otherwise, we'll be
// creating the XML using the "non enterprise" value.
if (getTargetAppName() == null) {
fieldValue = (String)getValueFromObject(xeo, fieldName);
}
else {
fieldValue = (String)getValueFromObject(xeo, getTargetAppName(), fieldName);
}
logger.debug("[buildElement] Adding Element (" + fieldName + ") with a value of " +
fieldValue + " to Element " + elementName + " passed in.");
eOutput.addContent(new Element(fieldName).setText(fieldValue));
}
}
else if (fieldType.equals("Attribute")) {
StringBuffer sBuf = new StringBuffer();
sBuf.append(fieldName.substring(0,1).toLowerCase());
sBuf.append(fieldName.substring(1));
String attrName = new String(sBuf);
Object obj = null;
if (getTargetAppName() == null) {
obj = getValueFromObject(xeo, fieldName);
}
else {
obj = getValueFromObject(xeo, getTargetAppName(), fieldName);
}
if (obj instanceof String) {
fieldValue = (String)obj;
}
else {
}
if (fieldValue == null || fieldValue.trim().length() == 0) {
// Have to check the parent field (if one exists) for situations
// where the Parent field isn't required but children of that Parent
// are. Example is DeceasedDate in BasicPerson. DeceasedDate is optional
// but Month,Day,Year are required if DeceasedDate is present...
// 8/6/01 - this won't work for things like Address@type, type is required but Address isn't
// need to figure out a better way to do this.....
if (isRequired && parentIsRequired && xeo.getEnterpriseFields().ignoreValidation() == false) {
throw new EnterpriseLayoutException("The " + fieldType + " " +
elementName + "/" + fieldName + " is required but it has no value.");
}
else {
logger.debug(fieldName + " on " + elementName + " is required but " + elementName + " isn't so we can ignore this missing field.");
// Don't even add the Attribute since it's not required and there is no
// data in the object's field.
return;
}
}
// Have to do this to validate the data since the object we're dealing
// with may not have had an EnterpriseFields object when it was populated.
try {
String tempValue = removeXmlEscapes(fieldValue);
setVariableValue(xeo, fieldName, tempValue , String.class);
}
catch (Exception e) {
throw new EnterpriseLayoutException("Error setting " + objectName + "/" + fieldName +
" to the value " + fieldValue + " Exception: " + e.getMessage(), e);
}
// Now, we have to get the value from the object again since it may have been
// modified when the setter methods are called above. Otherwise, we'll be
// creating the XML using the "non enterprise" value.
if (getTargetAppName() == null) {
fieldValue = (String)getValueFromObject(xeo, fieldName);
}
else {
fieldValue = (String)getValueFromObject(xeo, getTargetAppName(), fieldName);
}
logger.debug("[buildElement] Adding Attribute (" + attrName + ") with a value of " +
fieldValue + " to Element " + elementName + " passed in.");
eOutput.setAttribute(attrName, fieldValue);
}
else if (fieldType.equals("Object")) {
// If we can't find the ObjectDefinition as a child,
// look for it at the root of the document. If it's
// not there, then we have an error situation.
Element eObjDef = eField.getChild("ObjectDefinition");
if (eObjDef == null) {
eObjDef = getLayout(getLayoutRoot(), fieldName);
if (eObjDef == null) {
throw new EnterpriseLayoutException("Can't find an ObjectDefinition in the Layout Document for " +
objectName + "/" + fieldName);
}
}
java.util.List lFields = eObjDef.getChildren("Field");
String layoutName = eObjDef.getAttribute("name").getValue();
logger.debug("There are " + lFields.size() + " Fields in the " + layoutName + " Layout.");
if (isRepeating(xeo, fieldName)) {
int numObjects = getLength(xeo, fieldName);
java.util.List sortedIndexes = getSortedIndexes(xeo, numObjects, fieldName);
logger.debug("XmlLayout: sortedIndexes.size: " + sortedIndexes.size());
for (int i=0; i<sortedIndexes.size(); i++) {
String nextIndex = (String)sortedIndexes.get(i);
logger.debug("Sorted Index [" + i + "] is " + nextIndex);
Integer iParm = new Integer(nextIndex);
// the rest is the same????
Object obj = null;
obj = getValueFromObject(xeo, fieldName, new Object[] {iParm}, new Class[] {Integer.TYPE});
if (obj instanceof XmlEnterpriseObject) {
XmlEnterpriseObject aNewXeo = (XmlEnterpriseObject)obj;
try {
aNewXeo.setEnterpriseFields((EnterpriseFields)xeo.getEnterpriseFields().clone());
}
catch (Exception e) {
throw new EnterpriseLayoutException("Exception cloning EnterpriseFields object for object " + objectName + "." +
" Exception: " + e.getMessage(), e);
}
String newXeoClassName = aNewXeo.getClass().getName();
String newXeoObjectName = newXeoClassName.substring(newXeoClassName.lastIndexOf('.') + 1);
Element eChildOutput = new Element(fieldName);
for (int j=0; j<lFields.size(); j++) {
Element eChildField = (Element)lFields.get(j);
buildElement(aNewXeo, eChildOutput, eChildField, isRequired);
}
logger.debug("[buildElement] Adding [" + i + "] Element " + fieldName + " to the " + elementName + " passed in.");
eOutput.addContent(eChildOutput);
}
else {
// should never get here.
}
}
}
else {
Object obj = null;
obj = getValueFromObject(xeo, fieldName);
if (obj instanceof XmlEnterpriseObject) {
XmlEnterpriseObject aNewXeo = (XmlEnterpriseObject)obj;
try {
aNewXeo.setEnterpriseFields((EnterpriseFields)xeo.getEnterpriseFields().clone());
}
catch (Exception e) {
throw new EnterpriseLayoutException("Exception cloning EnterpriseFields object for object " + objectName + "." +
" Exception: " + e.getMessage(), e);
}
try {
if (aNewXeo.isEmpty()) {
logger.debug("No need to try and add the " + fieldName + " to the element passed in.");
if (isRequired == false || parentIsRequired == false) {
return;
}
}
else {
logger.debug("Object " + fieldName + " isn't empty so we have to try and add it.");
}
}
catch (XmlEnterpriseObjectException e) {
throw new EnterpriseLayoutException("Error checking if object " + objectName + "/" +
fieldName + " is empty. Exception: " + e.getMessage(), e);
}
String newXeoClassName = aNewXeo.getClass().getName();
String newXeoObjectName = newXeoClassName.substring(newXeoClassName.lastIndexOf('.') + 1);
Element eChildOutput = new Element(fieldName);
for (int i=0; i<lFields.size(); i++) {
Element eChildField = (Element)lFields.get(i);
buildElement(aNewXeo, eChildOutput, eChildField, isRequired);
}
logger.debug("[buildElement] Adding Element " + fieldName + " to the " + elementName + " passed in.");
eOutput.addContent(eChildOutput);
}
else {
logger.debug("[buildElement] the field " + fieldName + " is not an XmlEnterpriseObject...");
}
}
}
}
/**
* This method ensures that repeatable fields of type 'Object' are always seriallized
* in the same order sorted by combined key value for that object. This way, the equals
* method in XmlEnterpriseObject should evaluate to true instead of false which is
* what will happen if the objects aren't serialized in a consistent order.
*<P>
* @param xeo XmlEnterpriseObject the parent xeo that we'll be retrieving child objects from
* @param numObjects int the number of child objects (by this name) that exist in the parent object
* @param childFieldName String the child objects name (field name)
*<P>
* @return java.util.List a list of indexes that correspond to the order inwhich the child
* objects should be retrieved from the parent object to ensure they're being retrieved consistently
* by combined key value.
*<P>
* @throws EnterpriseLayoutManagerException if an error occurs while sorting the child objects
* and building the return List.
**/
private java.util.List getSortedIndexes(XmlEnterpriseObject xeo, int numObjects, String childFieldName) throws EnterpriseLayoutException {
String[] sortedKeyValues = new String[numObjects];
// add all the combined key values to a sortable array of strings.
for (int i=0; i<numObjects; i++) {
Integer iParm = new Integer(i);
Object obj = null;
obj = getValueFromObject(xeo, childFieldName, new Object[] {iParm}, new Class[] {Integer.TYPE});
if (obj instanceof XmlEnterpriseObject) {
XmlEnterpriseObject aNewXeo = (XmlEnterpriseObject)obj;
String combinedKeyValue = null;
try {
combinedKeyValue = aNewXeo.getCombinedKeyValue();
if (combinedKeyValue != null && combinedKeyValue.length() > 0) {
logger.debug("getSortedIndexes: adding combined key of '" + combinedKeyValue + " - " + i + "' to sortedKeyValues Array.");
sortedKeyValues[i] = combinedKeyValue + " - " + i; // incase there are multiple instances of the same combined key value
}
else {
sortedKeyValues[i] = Integer.toString(i);
}
}
catch (Exception e) {
throw new EnterpriseLayoutException(e.getMessage(), e);
}
}
}
// sort the array of keyvalues.
Arrays.sort(sortedKeyValues);
logger.debug("getSortedIndexes: sortedKeyValues size: " + sortedKeyValues.length);
// now build a list of indexes that correspond to the sorted combinedKeyValues
ArrayList sortedIndexes = new ArrayList();
for (int i=0; i<sortedKeyValues.length; i++) {
String sortedKeyValue = sortedKeyValues[i];
boolean foundMatch = false;
keyTest: for (int j=0; j<numObjects; j++) {
Integer iParm = new Integer(j);
Object obj = null;
obj = getValueFromObject(xeo, childFieldName, new Object[] {iParm}, new Class[] {Integer.TYPE});
if (obj instanceof XmlEnterpriseObject) {
XmlEnterpriseObject aNewXeo = (XmlEnterpriseObject)obj;
String combinedKeyValue = null;
try {
combinedKeyValue = aNewXeo.getCombinedKeyValue();
if (combinedKeyValue != null && combinedKeyValue.length() > 0) {
combinedKeyValue = combinedKeyValue + " - " + j; // incase there are multiple instances of the same combined key value
logger.debug("getSortedIndexes: comparing combinedKeyValue " + combinedKeyValue + " to " + sortedKeyValue);
}
else {
combinedKeyValue = Integer.toString(j);
}
if (combinedKeyValue.equalsIgnoreCase(sortedKeyValue)) {
logger.debug("getSortedIndexes: found a match!");
sortedIndexes.add(Integer.toString(j));
foundMatch = true;
break keyTest;
}
}
catch (Exception e) {
throw new EnterpriseLayoutException(e.getMessage(), e);
}
}
}
if (foundMatch == false) {
logger.fatal("getSortedIndexes: could not find a match for " + sortedKeyValue);
throw new EnterpriseLayoutException("Could not find sort the repeatable " +
childFieldName + " objects on the " + xeo.getClass().getName() + " object.");
}
}
return sortedIndexes;
}
private void buildObject(XmlEnterpriseObject xeo, Element eInput,
Element eField, boolean parentIsRequired)
throws EnterpriseLayoutException {
String className = xeo.getClass().getName();
String objectName = className.substring(className.lastIndexOf('.') + 1); // Our based element in the XML passed in
logger.debug("Name of Object passed in is " + objectName);
String fieldName = eField.getAttribute("name").getValue();
logger.debug("Field Name is " + fieldName);
String fieldType = eField.getAttribute("type").getValue();
logger.debug("Field Type is " + fieldType);
String fieldValue = "";
boolean isRequired = isRequired(eField);
if (fieldType.equals("Element")) {
java.util.List lValues = eInput.getChildren(fieldName);
if (lValues.size() == 0) {
// This means there were no elements with the name matching the
// variable "fieldName". If this is a required field, then this
// is an error. Otherwise, it's okay and we can skip the field.
if (isRequired && xeo.getEnterpriseFields().ignoreValidation() == false) {
throw new EnterpriseLayoutException(fieldName +
" is a required field but could not be found in the " +
objectName + " Element passed in.");
}
}
for (int j=0; j<lValues.size(); j++) {
Element eValue = (Element)lValues.get(j);
fieldValue = eValue.getText();
logger.debug("Setting " + fieldName + " to " + fieldValue + " on the " + objectName + " object passed in.");
try {
fieldValue = removeXmlEscapes(fieldValue);
setVariableValue(xeo, fieldName, fieldValue, String.class);
}
catch (Exception e) {
throw new EnterpriseLayoutException("Error setting " + objectName + "/" + fieldName +
" Exception: " + e.getMessage(), e);
}
}
}
else if (fieldType.equals("Attribute")) {
StringBuffer sBuf = new StringBuffer();
sBuf.append(fieldName.substring(0,1).toLowerCase());
sBuf.append(fieldName.substring(1));
String attrName = new String(sBuf);
Attribute anAttr = eInput.getAttribute(attrName);
if (anAttr == null) {
if (isRequired && xeo.getEnterpriseFields().ignoreValidation() == false) {
throw new EnterpriseLayoutException(fieldName +
" is a required field but could not be found in the " +
objectName + " Element passed in.");
}
}
else {
fieldValue = anAttr.getValue();
if (fieldValue == null || fieldValue.trim().length() == 0) {
if (isRequired && xeo.getEnterpriseFields().ignoreValidation() == false) {
throw new EnterpriseLayoutException(fieldName +
" is a required field but could not be found in the " +
objectName + " Element passed in.");
}
}
logger.debug("Setting " + fieldName + " to " + fieldValue + " on the " + objectName + " object passed in.");
try {
fieldValue = removeXmlEscapes(fieldValue);
setVariableValue(xeo, fieldName, fieldValue, String.class);
}
catch (Exception e) {
throw new EnterpriseLayoutException("Error setting " + objectName + "/" +
fieldName + " Exception:" + e.getMessage(), e);
}
}
}
else if (fieldType.equals("Object")) {
Element eObjDef = eField.getChild("ObjectDefinition");
if (eObjDef == null) {
logger.debug("Getting ObjectDefinition for " + fieldName + " from root...");
eObjDef = getLayout(getLayoutRoot(), fieldName);
if (eObjDef == null) {
throw new EnterpriseLayoutException("Can't find an ObjectDefinition in the Layout Document for " +
objectName + "/" + fieldName);
}
}
String objClassName = eObjDef.getChild("ClassName").getText();
String objObjectName = objClassName.substring(objClassName.lastIndexOf('.') + 1);
String layoutName = eObjDef.getAttribute("name").getValue();
/*
if (fieldName.equals("EffectiveDate")) {
logger.debug("Date class name is: " + objClassName);
}
*/
java.util.List lObjs = eInput.getChildren(fieldName);
logger.debug("[xmllayout] There are " + lObjs.size() + " " + fieldName + " elements in the " + objectName + " Element passed in.");
if (lObjs.size() == 0) {
if (isRequired && xeo.getEnterpriseFields().ignoreValidation() == false) {
throw new EnterpriseLayoutException(fieldName +
" is a required field but could not be found in the " +
objectName + " Element passed in.");
}
}
else {
XmlEnterpriseObject aNewXeo = null;
Element eObjInput = null;
for (int j=0; j<lObjs.size(); j++) {
eObjInput = (Element)lObjs.get(j);
if (elementIsEmpty(eObjInput) && isRequired == false) {
// If the element is empty and the object is not required,
// then there's no reason to instantiate and attempt to build
// the object.
logger.debug("No need to instantiate the " + fieldName + " object!");
return;
}
Class classType = null;
if (isDate(objClassName)) {
String oName = objClassName + "(" + layoutName + ")";
logger.debug("instantiating a " + oName);
aNewXeo = (XmlEnterpriseObject)instantiate(oName);
try {
aNewXeo.setEnterpriseFields((EnterpriseFields)xeo.getEnterpriseFields().clone());
}
catch (Exception e) {
throw new EnterpriseLayoutException("Exception cloning EnterpriseFields object for object " + objectName + "." +
" Exception: " + e.getMessage(), e);
}
classType = aNewXeo.getClass();
objectName = objectName + "(" + layoutName + ")";
}
else {
logger.debug("instantiating a " + objClassName);
aNewXeo = (XmlEnterpriseObject)instantiate(objClassName);
try {
aNewXeo.setEnterpriseFields((EnterpriseFields)xeo.getEnterpriseFields().clone());
}
catch (Exception e) {
throw new EnterpriseLayoutException("Exception cloning EnterpriseFields object for object " + objectName + "." +
" Exception: " + e.getMessage(), e);
}
classType = aNewXeo.getClass();
}
java.util.List lFields = eObjDef.getChildren("Field");
logger.debug("There are " + lFields.size() + " Fields in the " + layoutName + " Layout.");
for (int i=0; i<lFields.size(); i++) {
Element eObjField = (Element)lFields.get(i);
buildObject(aNewXeo, eObjInput, eObjField, isRequired);
}
logger.debug("Setting the " + fieldName + " object on the " + xeo.getClass().getName());
try {
/*
logger.info("xeo is " + xeo.getClass().getName());
logger.info("child is " + aNewXeo.getClass().getName());
logger.info("fieldName is " + fieldName);
*/
setVariableValue(xeo, fieldName, aNewXeo, classType);
}
catch (EnterpriseFieldException e) {
throw new EnterpriseLayoutException("Error setting " + objectName + "/" + fieldName + " Exception:" + e.getMessage(), e);
}
}
}
}
else {
throw new EnterpriseLayoutException("Invalid field type " +
fieldType + " for field named " + objectName + "/" + fieldName);
}
}
private boolean elementIsEmpty(Element e) {
boolean retVal = true;
java.util.List attrs = e.getAttributes();
for (int i=0; i<attrs.size(); i++) {
Attribute a = (Attribute)attrs.get(i);
if (a.getValue().length() > 0) {
logger.debug(e.getName() + " has attributes with values, must instantiate.");
return false;
}
}
java.util.List children = e.getChildren();
for (int i=0; i<children.size(); i++) {
Element eChild = (Element)children.get(i);
if (eChild.getContentSize()>0) {
retVal = elementIsEmpty(eChild);
if (retVal) {
logger.debug(e.getName() + " has a child with values, must instantiate.");
return false;
}
}
else {
if (eChild.getText().length() > 0) {
logger.debug("Child element " + eChild.getName() + " of " + e.getName() +
" has a data in it, must instantiate.");
return false;
}
}
}
logger.debug(e.getName() + " doesn't have any data in it");
return retVal;
}
private String removeXmlEscapes(String value) throws REException {
/*
Look at the value and replace any Entity References
with the appropriate values. The special characters are:
& (replaced with &)
> (replaced with >)
< (replaced with <)
" (replaced with ")
' (replaced with ')
This is necessary because the parser might potential croak
if any of our data contains these characters.
*/
String escapedValue = "";
if (value != null || value.length() > 0) {
RE gnuRegExp = new RE("\\&");
escapedValue = gnuRegExp.substituteAll(value,"&");
RE gnuRegExp2 = new RE("\\<");
escapedValue = gnuRegExp2.substituteAll(escapedValue,"<");
RE gnuRegExp3 = new RE("\\>");
escapedValue = gnuRegExp3.substituteAll(escapedValue,">");
RE gnuRegExp4 = new RE("\\"");
escapedValue = gnuRegExp4.substituteAll(escapedValue,"\"");
RE gnuRegExp5 = new RE("\\'");
escapedValue = gnuRegExp5.substituteAll(escapedValue,"'");
}
return escapedValue;
}
}