0001: /*
0002: * The contents of this file are subject to the
0003: * Mozilla Public License Version 1.1 (the "License");
0004: * you may not use this file except in compliance with the License.
0005: * You may obtain a copy of the License at http://www.mozilla.org/MPL/
0006: *
0007: * Software distributed under the License is distributed on an "AS IS"
0008: * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
0009: * See the License for the specific language governing rights and
0010: * limitations under the License.
0011: *
0012: * The Initial Developer of the Original Code is Simulacra Media Ltd.
0013: * Portions created by Simulacra Media Ltd are Copyright (C) Simulacra Media Ltd, 2004.
0014: *
0015: * All Rights Reserved.
0016: *
0017: * Contributor(s):
0018: */
0019: package org.openharmonise.rm.metadata;
0020:
0021: import java.sql.*;
0022: import java.util.*;
0023: import java.util.logging.*;
0024:
0025: import org.openharmonise.commons.cache.*;
0026: import org.openharmonise.commons.dsi.*;
0027: import org.openharmonise.commons.dsi.dml.*;
0028: import org.openharmonise.commons.xml.XMLUtils;
0029: import org.openharmonise.rm.*;
0030: import org.openharmonise.rm.dsi.DataStoreObject;
0031: import org.openharmonise.rm.factory.*;
0032: import org.openharmonise.rm.publishing.*;
0033: import org.openharmonise.rm.resources.*;
0034: import org.openharmonise.rm.resources.lifecycle.EditException;
0035: import org.openharmonise.rm.resources.metadata.properties.*;
0036: import org.openharmonise.rm.resources.metadata.properties.domains.Domain;
0037: import org.openharmonise.rm.resources.publishing.Template;
0038: import org.w3c.dom.*;
0039:
0040: /**
0041: * Abstract class to handle the basic functionality for an instance of a
0042: * <code>Property</code>.
0043: *
0044: * @author Michael Bell
0045: * @version $Revision: 1.5 $
0046: *
0047: */
0048: public abstract class AbstractPropertyInstance implements Cloneable,
0049: DataStoreObject, Publishable {
0050:
0051: /**
0052: * The comment associated with this version of the property instance
0053: */
0054: private String m_sVersionComment;
0055:
0056: /**
0057: * The suffix attached to the historical property instance database
0058: * table name
0059: */
0060: protected static final String EXT_HIST = "_hist";
0061:
0062: /**
0063: * Array of valid operators which can be used for comparing against
0064: * property instance values
0065: */
0066: private static final String[] saValidOperators = { ">", "<", "=",
0067: ">=", "<=", "!=", "<>", "IN", "CONTAINS", "BETWEEN",
0068: "STARTS_WITH", "LOGINEQUALS", "LOGINNOT", "NOT IN", "NULL" };
0069:
0070: // DB constants
0071: /**
0072: * Id database column name
0073: */
0074: protected static final String CLMN_ID = "id";
0075:
0076: /**
0077: * Profile id database column name
0078: */
0079: protected static final String CLMN_PROFILE_ID = "profile_id";
0080:
0081: /**
0082: * Property id database column name
0083: */
0084: protected static final String CLMN_PROPERTY_ID = "property_id";
0085:
0086: /**
0087: * Version comment database column name
0088: */
0089: protected static final String CLMN_VERSION_COMMENT = "version_comment";
0090:
0091: /**
0092: * Property instance id sequence name
0093: */
0094: protected static final String SEQ_PROFILE_DATA = "seq_profile_data";
0095:
0096: //XML constants
0097: /**
0098: * Object type attribute name
0099: */
0100: public static final String ATTRIB_OBJECT_TYPE = "objectType";
0101:
0102: /**
0103: * Property id attribute name
0104: */
0105: public static final String ATTRIB_PROPERTY_ID = "propId";
0106:
0107: /**
0108: * Operator attribute name
0109: */
0110: public static final String ATTRIB_OPERATOR = "operator";
0111:
0112: /**
0113: * Property instance values tag name
0114: */
0115: public static final String TAG_PROP_INSTANCE_VALUES = "PropertyInstanceValues";
0116: /**
0117: * Property instance tag name
0118: */
0119: public static final String TAG_PROPERTYINSTANCE = "PropertyInstance";
0120:
0121: /**
0122: * Generic property instance value tag name
0123: */
0124: public static final String TAG_VALUE = "Value";
0125:
0126: /**
0127: * Attach XML element name
0128: */
0129: public static final String TAG_ATTACH = "Attach";
0130:
0131: /**
0132: * Detach XML element name
0133: */
0134: public static final String TAG_DETACH = "Detach";
0135:
0136: // Attributes
0137: /**
0138: * The owning profile
0139: */
0140: //TODO this creates a cyclic dependency and so another solution
0141: //would be preferable
0142: protected Profile m_profile = null;
0143:
0144: /**
0145: * A weak reference to the <code>Property</code> this instance is an
0146: * instance of
0147: */
0148: protected CachePointer m_property_ptr = null;
0149:
0150: /**
0151: * The list of property instance values
0152: */
0153: protected List m_values = null;
0154:
0155: /**
0156: * The list of property instance values to be saved on the next save
0157: * operation
0158: */
0159: protected List m_values2Add = null;
0160:
0161: /**
0162: * The list of property instance values to be removed on the
0163: * next save operation
0164: */
0165: protected List m_values2Remove = null;
0166:
0167: /**
0168: * The list of ids corresponding to the current list of
0169: * property instance values
0170: */
0171: protected List m_valueIds = null;
0172:
0173: /**
0174: * The current operator to be used in any value comparisons
0175: */
0176: protected String m_sOperator = "=";
0177:
0178: /**
0179: * <code>boolean</code> flag which indicates whether this property instance
0180: * can be saved to the database
0181: */
0182: protected boolean m_bIsTemporary = false;
0183:
0184: /**
0185: * The data store interface
0186: */
0187: protected AbstractDataStoreInterface m_dsi = null;
0188:
0189: /**
0190: * <code>boolean</code> flag which indicates whether any data has been
0191: * changed since the object was populated from the database
0192: */
0193: private boolean m_bIsChanged = false;
0194:
0195: /**
0196: * <code>boolean</code> flag which indicates whether this object
0197: * has been populated from the database
0198: */
0199: private boolean m_bIsPopulated = false;
0200:
0201: /**
0202: * <code>boolean</code> flag which indicates whether this object is
0203: * a historical version
0204: */
0205: private boolean m_bIsHistorical = false;
0206:
0207: /**
0208: * The database table holding data for this property instance
0209: */
0210: protected String m_sDataTable = null;
0211:
0212: /**
0213: * <code>String</code> constant for unknown data values
0214: */
0215: private static final String CONST_UNKNOWN = "unknown";
0216:
0217: /**
0218: * Logger for this class
0219: */
0220: private static final Logger m_logger = Logger
0221: .getLogger(AbstractPropertyInstance.class.getName());
0222:
0223: //initialiser block
0224: {
0225: m_values = new Vector();
0226: m_valueIds = new Vector();
0227: m_values2Add = new Vector();
0228: m_values2Remove = new Vector();
0229: }
0230:
0231: /**
0232: * Constructs a property instance
0233: */
0234: public AbstractPropertyInstance() {
0235: }
0236:
0237: /**
0238: * Constructs a property instance with an interface to the data store.
0239: *
0240: * @param dbint the data store interface
0241: */
0242: public AbstractPropertyInstance(AbstractDataStoreInterface dbint) {
0243: m_dsi = dbint;
0244: }
0245:
0246: /**
0247: * Constructs a property instance with an interface to
0248: * the data store and a reference to the <code>Profile</code> which
0249: * will contain this property instance.
0250: *
0251: * @param dbintrf the data store interface
0252: * @param profile the owner <code>Profile</code>
0253: */
0254: public AbstractPropertyInstance(AbstractDataStoreInterface dbintrf,
0255: Profile profile) {
0256: this (dbintrf);
0257: m_profile = profile;
0258: setDBTable(profile);
0259: }
0260:
0261: /**
0262: * Constructs a property instance of the specified <code>Property</code>
0263: * with an interface to the data store.
0264: *
0265: * @param dbintrf the data store interface
0266: * @param prop the <code>Property</code> that this object is an instance of
0267: */
0268: public AbstractPropertyInstance(AbstractDataStoreInterface dbintrf,
0269: Property prop) {
0270: this (dbintrf);
0271:
0272: try {
0273: m_property_ptr = CacheHandler.getInstance(m_dsi)
0274: .getCachePointer(prop);
0275: } catch (CacheException e) {
0276: m_logger.log(Level.WARNING, e.getLocalizedMessage(), e);
0277: }
0278: }
0279:
0280: /**
0281: * Constructs a property instance with an interface to the data store,
0282: * a reference to the <code>Profile</code> which will contain this
0283: * property instance and a reference to the <code>Property</code>
0284: * identified by the specified id.
0285: *
0286: * @param dbintrf the data store interface
0287: * @param nPropertyId the id of the <code>Property</code> that this object is an instance of
0288: * @param profile the owner <code>Profile</code>
0289: */
0290: public AbstractPropertyInstance(AbstractDataStoreInterface dbintrf,
0291: int nPropertyId, Profile profile) {
0292: this (dbintrf);
0293: Property prop = new Property(dbintrf, nPropertyId);
0294: try {
0295: m_property_ptr = CacheHandler.getInstance(m_dsi)
0296: .getCachePointer(prop);
0297: } catch (CacheException e) {
0298: m_logger.log(Level.WARNING, e.getLocalizedMessage(), e);
0299: }
0300: m_profile = profile;
0301: setDBTable(profile);
0302: }
0303:
0304: /**
0305: * Constructs a property instance with an interface to the data store,
0306: * a reference to the <code>Profile</code> which will contain this
0307: * property instance and a reference to the <code>Property</code>
0308: * which this object is an instance of.
0309: *
0310: * @param dbintrf the data store interface
0311: * @param property the <code>Property</code>
0312: * @param profile the owner <code>Profile</code>
0313: */
0314: public AbstractPropertyInstance(AbstractDataStoreInterface dbintrf,
0315: Property property, Profile profile) {
0316: this (dbintrf, property);
0317: m_profile = profile;
0318: setDBTable(profile);
0319: }
0320:
0321: /**
0322: * Sets profile data table name.
0323: *
0324: * @param profile the profile data table name
0325: */
0326: abstract protected void setDBTable(Profile profile);
0327:
0328: /**
0329: * Returns the <code>Property</code> for which this object is
0330: * an instance.
0331: *
0332: * @return the <code>Property</code> for which this object is
0333: * an instance
0334: */
0335: public Property getProperty() throws DataAccessException {
0336: Property prop = null;
0337: try {
0338: if (m_property_ptr != null) {
0339: prop = (Property) m_property_ptr.getObject();
0340:
0341: if (prop.exists() == false) {
0342: m_logger
0343: .logp(
0344: Level.INFO,
0345: this .getClass().getName(),
0346: "getProperty",
0347: "Property "
0348: + m_property_ptr.getKey()
0349: + " for property instance no longer exists");
0350: prop = null;
0351: }
0352: }
0353:
0354: } catch (CacheException e) {
0355: throw new DataAccessException(e.getLocalizedMessage(), e);
0356: }
0357:
0358: return prop;
0359: }
0360:
0361: /**
0362: * Sets the <code>Property</code> for which this object is
0363: * an instance.
0364: *
0365: * @param prop the <code>Property</code> for which this object is
0366: * an instance
0367: */
0368: public void setProperty(Property prop) throws PopulateException {
0369: try {
0370: m_property_ptr = CacheHandler.getInstance(m_dsi)
0371: .getCachePointer(prop);
0372: } catch (CacheException e) {
0373: throw new PopulateException(e.getLocalizedMessage(), e);
0374: }
0375: }
0376:
0377: /**
0378: * Sets the <code>Profile</code> which will contain this property
0379: * instance.
0380: *
0381: * @param prof the <code>Profile</code> which will contain this property
0382: * instance
0383: */
0384: public void setProfile(Profile prof) {
0385: this .m_profile = prof;
0386: setDBTable(prof);
0387: }
0388:
0389: /**
0390: * Returns the <code>Profile</code> which contains this property instance.
0391: *
0392: * @return the <code>Profile</code> this property instance is contained by
0393: *
0394: */
0395: public Profile getProfile() {
0396: return m_profile;
0397: }
0398:
0399: /**
0400: * Returns <code>true</code> if this property instance is temporary
0401: * and will therefore not be saved to the database.
0402: *
0403: * @return <code>true</code> if this property instance is temporary
0404: */
0405: public boolean isTemporary() {
0406: return m_bIsTemporary;
0407: }
0408:
0409: /**
0410: * Sets whether this property instance is temporary. A temporary
0411: * property instance is not saved to the database.
0412: *
0413: * @return bIsTemporary <code>true</code> if this property instance
0414: * is to be temporary, otherwise <code>false</code>
0415: */
0416: public void setIsTemporary(boolean bIsTemporary) {
0417: m_bIsTemporary = bIsTemporary;
0418: }
0419:
0420: /**
0421: * Sets the comment associated to this version of the object.
0422: *
0423: * @param sComment the version comment
0424: */
0425: public void setVersionComment(String sComment) {
0426: m_sVersionComment = sComment;
0427: }
0428:
0429: /**
0430: * Returns the comment associated with this version.
0431: *
0432: * @return the comment associated with this version
0433: */
0434: public String getVersionComment() {
0435: return m_sVersionComment;
0436: }
0437:
0438: /**
0439: * Returns the operator associated to this property instance, for use
0440: * in comparisons.
0441: *
0442: * @return the operator associated to this property instance
0443: */
0444: public String getOperator() {
0445: return m_sOperator;
0446: }
0447:
0448: /**
0449: * Returns the name of the <code>Property</code> this object is an
0450: * instance of.
0451: *
0452: * @return the name of the <code>Property</code> this object is an
0453: * instance of.
0454: * @throws DataAccessException if there is an error accessing the
0455: * name of the <code>Property</code>
0456: */
0457: public String getName() throws DataAccessException {
0458: String sName = null;
0459: Property prop = getProperty();
0460:
0461: if (prop != null) {
0462: sName = prop.getName();
0463: }
0464:
0465: return sName;
0466: }
0467:
0468: /* (non-Javadoc)
0469: * @see org.openharmonise.rm.dsi.DataStoreObject#getDBTableName()
0470: */
0471: public String getDBTableName() {
0472:
0473: return m_sDataTable;
0474: }
0475:
0476: /**
0477: * Sets the operator to be used in comparisons involving this
0478: * property instance.
0479: *
0480: * @param sOperator the operator to be used in comparisons involving
0481: * this property instance
0482: */
0483: public void setOperator(String sOperator) {
0484: m_sOperator = sOperator;
0485: }
0486:
0487: /**
0488: * Returns the list values of this property instance.
0489: *
0490: * @return the list values of this property instance
0491: */
0492: public List getValues() {
0493: return m_values;
0494: }
0495:
0496: /**
0497: * Sets the values of this instance.
0498: *
0499: * @param values a list of values
0500: */
0501: public void setValues(List values)
0502: throws InvalidPropertyValueException {
0503: m_values = new Vector();
0504: m_values.addAll(values);
0505:
0506: if (values.size() != 0) {
0507: if (m_bIsPopulated == true) {
0508: setIsChanged(true);
0509: }
0510: }
0511: }
0512:
0513: /**
0514: * Removes the specified value from this instance's list of values.
0515: *
0516: * @param val the value to remove
0517: */
0518: public void removeValue(Object val) {
0519: if (m_values != null && m_values.contains(val) == true) {
0520: m_values.remove(val);
0521:
0522: if (m_bIsPopulated == true) {
0523: setIsChanged(true);
0524: m_values2Remove.add(val);
0525: }
0526: }
0527: }
0528:
0529: /**
0530: *
0531: * Returns the first value of this property instance's values.
0532: * If there are no values a <code>null</code> is returned.
0533: *
0534: * @return the first value in the list of values
0535: */
0536: public Object getValue() {
0537: Object objReturn = null;
0538:
0539: if (m_values != null && m_values.size() > 0) {
0540: objReturn = m_values.get(0);
0541: }
0542:
0543: return objReturn;
0544: }
0545:
0546: /**
0547: * Clears the list of values.
0548: */
0549: public void clearValues() {
0550: if (m_values != null) {
0551: m_values.clear();
0552: }
0553: }
0554:
0555: /**
0556: * Returns <code>true</code> if this object has values.
0557: *
0558: * @return <code>true</code> if this object has values.
0559: */
0560: public boolean hasValues() {
0561: boolean bReturn = true;
0562:
0563: if ((m_values == null) || (m_values.size() == 0)) {
0564: bReturn = false;
0565: }
0566:
0567: return bReturn;
0568: }
0569:
0570: /**
0571: * Returns a value at the specified index in the list of values.
0572: * Returns <code>null</code> if no value exists at that index.
0573: *
0574: * @return the value at the specified index
0575: */
0576: public Object getValue(int nIndex) {
0577: Object objReturn = null;
0578:
0579: if (m_values != null) {
0580: if (m_values.size() > nIndex) {
0581: objReturn = m_values.get(nIndex);
0582: }
0583: }
0584:
0585: return objReturn;
0586: }
0587:
0588: /**
0589: * Merge the given property instance with this property instance.
0590: *
0591: * @param the property instance to merge with this instance
0592: */
0593: public boolean merge(AbstractPropertyInstance property) {
0594: boolean bReturn = false;
0595:
0596: if (m_property_ptr.equals(property.m_property_ptr)) {
0597: int nNumberValues = property.getValues().size();
0598:
0599: for (int i = 0; i < nNumberValues; i++) {
0600: if (m_values.contains(property.getValue(i)) == false) {
0601: m_values.add(property.getValue(i));
0602: bReturn = true;
0603: }
0604: }
0605: }
0606:
0607: return bReturn;
0608: }
0609:
0610: /* (non-Javadoc)
0611: * @see java.lang.Object#equals(java.lang.Object)
0612: */
0613: public boolean equals(Object obj) {
0614: boolean bRtn = false;
0615:
0616: AbstractPropertyInstance propInstance = (AbstractPropertyInstance) obj;
0617:
0618: if ((propInstance.hasValues() == false)
0619: && (this .hasValues() == false)) {
0620: bRtn = true;
0621: } else if (propInstance.hasValues() != this .hasValues()) {
0622: bRtn = false;
0623: } else {
0624: if (m_property_ptr.equals(propInstance.m_property_ptr)) {
0625: List vLocalValues = this .getValues();
0626: List vRemoteValues = propInstance.getValues();
0627:
0628: if (vLocalValues.containsAll(vRemoteValues)
0629: && vRemoteValues.containsAll(vLocalValues)) {
0630: bRtn = true;
0631: }
0632: }
0633: }
0634:
0635: return bRtn;
0636: }
0637:
0638: /**
0639: * Returns <code>true</code> if this object matches the
0640: * given <code>AbstractPropertyInstance</code>.
0641: *
0642: * @param propInst the property instance to match
0643: * @return <code>true</code> if this object matches the
0644: * given <code>AbstractPropertyInstance</code>.
0645: * @throws ProfileException if an error occurs
0646: */
0647: abstract public boolean match(AbstractPropertyInstance propInst)
0648: throws ProfileException;
0649:
0650: /* (non-Javadoc)
0651: * @see java.lang.Object#clone()
0652: */
0653: public Object clone() {
0654:
0655: AbstractPropertyInstance other = null;
0656: //Note: if exception is thrown it is gonna cause problems with
0657: //saving, etc so I've elected to throw a RuntimeException
0658: try {
0659:
0660: other = (AbstractPropertyInstance) super .clone();
0661:
0662: //setValues clones vector
0663: if (m_values != null) {
0664:
0665: other.m_values = new Vector();
0666:
0667: Iterator iter = m_values.iterator();
0668:
0669: while (iter.hasNext()) {
0670: Object objVal = (Object) iter.next();
0671:
0672: if (objVal instanceof AbstractObject) {
0673: other.m_values.add(((AbstractObject) objVal)
0674: .clone());
0675: } else {
0676: other.m_values.add(objVal);
0677: }
0678: }
0679:
0680: if (m_valueIds != null) {
0681: other.setValueIds(m_valueIds);
0682: }
0683: }
0684:
0685: if (m_values2Add != null) {
0686: other.m_values2Add = new Vector(m_values2Add);
0687: }
0688:
0689: if (m_values2Remove != null) {
0690: other.m_values2Remove = new Vector(m_values2Remove);
0691: }
0692: } catch (CloneNotSupportedException cu_e) {
0693: m_logger.log(Level.WARNING, cu_e.getLocalizedMessage(),
0694: cu_e);
0695: throw new IllegalStateException(
0696: "Problem occured during clone:"
0697: + cu_e.getLocalizedMessage());
0698: }
0699:
0700: return other;
0701: }
0702:
0703: /* (non-Javadoc)
0704: * @see org.openharmonise.rm.dsi.DataStoreObject#getInstanceColumnRef(java.lang.String, boolean)
0705: */
0706: public ColumnRef getInstanceColumnRef(String sColumn,
0707: boolean bIsHist) throws DataStoreException {
0708: ColumnRef returnColRef = null;
0709: String sDBTable = getDBTableName();
0710:
0711: if (sColumn.equals(AbstractObject.ATTRIB_ID) == true
0712: || sColumn.equals(CLMN_ID) == true) {
0713: returnColRef = new ColumnRef(sDBTable, CLMN_ID,
0714: ColumnRef.NUMBER);
0715: } else if (sColumn.equals(CLMN_PROFILE_ID) == true
0716: || sColumn.equals(Profile.TAG_PROFILE) == true) {
0717: returnColRef = new ColumnRef(sDBTable, CLMN_PROFILE_ID,
0718: ColumnRef.NUMBER);
0719: } else if (sColumn.equals(CLMN_PROPERTY_ID) == true
0720: || sColumn.equals(Property.TAG_PROPERTY) == true) {
0721: returnColRef = new ColumnRef(sDBTable, CLMN_PROPERTY_ID,
0722: ColumnRef.NUMBER);
0723: } else if (sColumn
0724: .equals(AbstractEditableObject.TAG_VERSION_COMMENT) == true
0725: || sColumn.equals(CLMN_VERSION_COMMENT) == true) {
0726: returnColRef = new ColumnRef(sDBTable,
0727: CLMN_VERSION_COMMENT, ColumnRef.TEXT);
0728: }
0729:
0730: if (returnColRef != null) {
0731: return returnColRef;
0732: } else {
0733: throw new InvalidColumnReferenceException(sColumn);
0734: }
0735: }
0736:
0737: /* (non-Javadoc)
0738: * @see org.openharmonise.rm.publishing.Publishable#publish(org.openharmonise.rm.resources.publishing.Template, org.openharmonise.rm.publishing.HarmoniseOutput, org.openharmonise.rm.publishing.State)
0739: */
0740: public Element publish(Template template, HarmoniseOutput xmlDoc,
0741: State state) throws PublishException {
0742: Element resultEl = null;
0743:
0744: try {
0745:
0746: resultEl = publish(template.getTemplateRootElement(),
0747: xmlDoc, state);
0748: } catch (Exception e) {
0749: throw new PublishException(
0750: "Error occured while publishing", e);
0751: }
0752:
0753: return resultEl;
0754: }
0755:
0756: /* (non-Javadoc)
0757: * @see org.openharmonise.rm.publishing.Publishable#publish(org.w3c.dom.Element, org.openharmonise.rm.publishing.HarmoniseOutput, org.openharmonise.rm.publishing.State)
0758: */
0759: public Element publish(Element formEl, HarmoniseOutput xmlDoc,
0760: State state) throws PublishException {
0761:
0762: Element el = null;
0763:
0764: String sTagname = formEl.getTagName();
0765:
0766: if (sTagname.equals(TAG_PROPERTYINSTANCE) == true) {
0767:
0768: el = xmlDoc.createElement(TAG_PROPERTYINSTANCE);
0769: String sPropName = CONST_UNKNOWN;
0770: try {
0771: sPropName = getProperty().getName();
0772: } catch (DataAccessException da_e) {
0773: m_logger.log(Level.WARNING, da_e.getLocalizedMessage(),
0774: da_e);
0775: sPropName = CONST_UNKNOWN;
0776: }
0777: Property prop = null;
0778:
0779: try {
0780: prop = getProperty();
0781: el.setAttribute(AbstractObject.ATTRIB_NAME, sPropName);
0782: el.setAttribute(AbstractObject.ATTRIB_ID,
0783: (String) m_property_ptr.getKey());
0784:
0785: el.setAttribute(ATTRIB_OBJECT_TYPE, prop.getRange()
0786: .getObject());
0787: } catch (DataAccessException e) {
0788: throw new PublishException(
0789: "Error occured acessing range object:"
0790: + e.getLocalizedMessage());
0791: }
0792:
0793: // copy all caption, error nodes etc (if any exist)
0794: Vector vIgnoreTags = new Vector();
0795: vIgnoreTags.add(TAG_PROP_INSTANCE_VALUES);
0796: vIgnoreTags.add(Property.TAG_PROPERTY);
0797: xmlDoc.copyChildren(el, formEl, vIgnoreTags);
0798:
0799: //now publish all child nodes
0800: NodeList nodes = formEl.getChildNodes();
0801:
0802: for (int i = 0; i < nodes.getLength(); i++) {
0803: if (nodes.item(i).getNodeType() == Node.ELEMENT_NODE) {
0804: Element tempEl = (Element) nodes.item(i);
0805: el.appendChild(publish(tempEl, xmlDoc, state));
0806: }
0807: }
0808:
0809: } else if (sTagname.equals(Property.TAG_PROPERTY)) {
0810:
0811: try {
0812: Property prop = getProperty();
0813:
0814: if (prop != null) {
0815: Element templateEl = (Element) XMLUtils
0816: .getFirstNamedChild(formEl,
0817: Template.TAG_TEMPLATE);
0818:
0819: Template template = null;
0820: if (templateEl != null) {
0821: try {
0822: template = (Template) HarmoniseObjectFactory
0823: .instantiateHarmoniseObject(m_dsi,
0824: templateEl, state);
0825: } catch (HarmoniseFactoryException e) {
0826: throw new PublishException(e
0827: .getLocalizedMessage(), e);
0828: }
0829: }
0830:
0831: if (template != null) {
0832: el = prop.publish(template, xmlDoc, state);
0833: } else {
0834: el = xmlDoc
0835: .createElement(Property.TAG_PROPERTY);
0836:
0837: el.setAttribute(AbstractObject.ATTRIB_ID,
0838: String.valueOf(prop.getId()));
0839:
0840: Element nameEl = xmlDoc
0841: .createElement(AbstractObject.TAG_NAME);
0842:
0843: nameEl.appendChild(xmlDoc.createTextNode(prop
0844: .getName()));
0845:
0846: el.appendChild(nameEl);
0847:
0848: Element displayNameEl = xmlDoc
0849: .createElement(AbstractChildObject.TAG_DISPLAY_NAME);
0850:
0851: displayNameEl.appendChild(xmlDoc
0852: .createTextNode(prop.getDisplayName()));
0853:
0854: el.appendChild(displayNameEl);
0855: }
0856: } else {
0857: el = xmlDoc.createElement(Property.TAG_PROPERTY);
0858: }
0859: } catch (DataAccessException e) {
0860: throw new PublishException(e.getLocalizedMessage(), e);
0861: }
0862: }
0863:
0864: return el;
0865: }
0866:
0867: /* (non-Javadoc)
0868: * @see org.openharmonise.rm.publishing.Publishable#populate(org.w3c.dom.Element, org.openharmonise.rm.publishing.State)
0869: */
0870: public void populate(Element xmlElement, State state)
0871: throws PopulateException {
0872: String sTagName = xmlElement.getTagName();
0873:
0874: try {
0875: if (sTagName.equals(TAG_PROPERTYINSTANCE) == true) {
0876: String sOperator = xmlElement
0877: .getAttribute(ATTRIB_OPERATOR);
0878: // get the operator
0879:
0880: if (sOperator != null && sOperator.length() > 0) {
0881: //set the operator for this property instance
0882: setOperator(sOperator);
0883: }
0884:
0885: // get the child nodes
0886: NodeList nodes = xmlElement.getChildNodes();
0887:
0888: for (int i = 0; i < nodes.getLength(); i++) {
0889: if (nodes.item(i).getNodeType() != Node.ELEMENT_NODE) {
0890: continue;
0891: }
0892:
0893: Element el = (Element) nodes.item(i);
0894: populate(el, state);
0895: }
0896: } else if (sTagName.equals(Property.TAG_PROPERTY) == true) {
0897: Element nameElm = XMLUtils.getFirstNamedChild(
0898: xmlElement, AbstractObject.TAG_NAME);
0899: Property prop = null;
0900:
0901: if (nameElm != null) {
0902: String sPropName = nameElm.getFirstChild()
0903: .getNodeValue();
0904: // get the value of the node
0905: prop = PropertyFactory.getPropertyFromName(m_dsi,
0906: sPropName);
0907: } else {
0908: prop = (Property) HarmoniseObjectFactory
0909: .instantiatePublishableObject(m_dsi,
0910: xmlElement, state);
0911: }
0912:
0913: prop.populate(xmlElement, state);
0914: // populate the property from the element
0915: setProperty(prop);
0916: // set this property to be the property for this property instance
0917: } else if (sTagName.equals(TAG_PROP_INSTANCE_VALUES) == true) {
0918: populate(xmlElement, state);
0919: } else {
0920: throw new PopulateException("Invalid Tag name: "
0921: + sTagName);
0922: }
0923: } catch (HarmoniseFactoryException e) {
0924: throw new PopulateException(
0925: "Error getting property from factory:", e);
0926: }
0927: }
0928:
0929: /*-------------------------------------------------------------------------------------
0930: Protected Methods
0931: --------------------------------------------------------------------------------------*/
0932:
0933: /**
0934: * Saves this property instance to the database.
0935: *
0936: * @param profile the owning <code>Profile</code> for this
0937: * property instance. The <code>Profile</code> determines which
0938: * database table the data get saved to.
0939: *
0940: * @throws ProfileException if the property instance can not be
0941: * saved due to a lack of either an associated <code>Profile</code>
0942: * or <code>Property</code>
0943: * @throws EditException if any other errors occur while saving data
0944: */
0945: protected void save(Profile prof) throws ProfileException,
0946: EditException {
0947: if ((m_bIsTemporary == false) && hasValues() == true) {
0948: //make sure database table is current
0949: setDBTable(prof);
0950:
0951: ResultSet rs = null;
0952: InsertStatement insert = new InsertStatement();
0953: int i = 0;
0954: String sTable = getDBTableName();
0955: boolean bIsHist = isHistorical();
0956:
0957: int nId = 0;
0958:
0959: m_valueIds.clear();
0960:
0961: for (i = 0; i < m_values.size(); i++) {
0962: Object objVal = m_values.get(i);
0963:
0964: insert.clear();
0965: try {
0966: if (i >= m_valueIds.size()) {
0967: nId = m_dsi
0968: .getSequenceNextValue(SEQ_PROFILE_DATA);
0969:
0970: m_valueIds.add(new Integer(nId));
0971: } else {
0972:
0973: nId = ((Integer) m_valueIds.get(i)).intValue();
0974:
0975: }
0976:
0977: int nPropId = getProperty().getId();
0978: int nProfId = prof.getId();
0979:
0980: //ensure we're inserting a valid property id
0981:
0982: if (nPropId <= 0) {
0983: throw new InvalidPropertyInstanceException(
0984: "Property had invalid id - "
0985: + m_property_ptr.getKey());
0986: }
0987:
0988: if (nProfId <= 0) {
0989: throw new InvalidPropertyInstanceException(
0990: "Profile had invalid id - " + nProfId);
0991: }
0992:
0993: insert.setTable(sTable);
0994:
0995: insert.addColumnValue(this .getInstanceColumnRef(
0996: CLMN_PROFILE_ID, bIsHist), nProfId);
0997: insert.addColumnValue(this .getInstanceColumnRef(
0998: CLMN_PROPERTY_ID, bIsHist), nPropId);
0999: insert.addColumnValue(this .getInstanceColumnRef(
1000: CLMN_ID, bIsHist), nId);
1001: insert.addColumnValue(this .getColumnForData(), this
1002: .getValueToStoreForValue(objVal));
1003: if (m_sVersionComment != null) {
1004:
1005: insert
1006: .addColumnValue(
1007: getInstanceColumnRef(
1008: AbstractEditableObject.TAG_VERSION_COMMENT,
1009: bIsHist),
1010: m_sVersionComment);
1011: }
1012:
1013: m_dsi.execute(insert);
1014:
1015: } catch (DataStoreException e) {
1016: throw new ProfileException(
1017: "Error occurred processing insert", e);
1018: } catch (SQLException sql_e) {
1019: throw new ProfileException(
1020: "Error occurred processing insert", sql_e);
1021: } catch (DataAccessException e) {
1022: throw new ProfileException(
1023: "Error occurred processing insert", e);
1024: }
1025: }
1026:
1027: m_values2Add.clear();
1028: m_values2Remove.clear();
1029:
1030: m_bIsChanged = false;
1031: m_bIsPopulated = true;
1032: }
1033: }
1034:
1035: /**
1036: * Updates this property instance in the database.
1037: *
1038: * @param prof the owning <code>Profile</code> for this property instance
1039: * @throws ProfileException if any errors occur saving the data
1040: * @throws EditException if any other errors occur saving the data
1041: */
1042: protected void update(Profile prof) throws ProfileException,
1043: EditException {
1044:
1045: if (isPopulated() == true && isChanged() == true) {
1046: InsertStatement insert = new InsertStatement();
1047: DeleteStatement delete = new DeleteStatement();
1048:
1049: int i = 0;
1050: String sTable = getDBTableName();
1051:
1052: boolean bIsHist = isHistorical();
1053: int nId = 0; // the next id
1054:
1055: // save the values to be saved
1056: for (i = 0; i < m_values2Add.size(); i++) {
1057: Object objVal = m_values2Add.get(i);
1058:
1059: insert.clear();
1060:
1061: try {
1062: if (isHistorical() == false) {
1063: nId = m_dsi
1064: .getSequenceNextValue(SEQ_PROFILE_DATA);
1065:
1066: m_valueIds.add(new Integer(nId));
1067: } else {
1068:
1069: nId = ((Integer) m_valueIds.get(i)).intValue();
1070:
1071: }
1072:
1073: insert.setTable(sTable);
1074:
1075: insert.addColumnValue(this .getInstanceColumnRef(
1076: CLMN_PROFILE_ID, bIsHist), prof.getId());
1077: insert.addColumnValue(this .getInstanceColumnRef(
1078: CLMN_PROPERTY_ID, bIsHist), getProperty()
1079: .getId());
1080: insert.addColumnValue(this .getInstanceColumnRef(
1081: CLMN_ID, bIsHist), nId);
1082: insert.addColumnValue(this .getColumnForData(), this
1083: .getValueToStoreForValue(objVal));
1084: if (m_sVersionComment != null) {
1085:
1086: insert
1087: .addColumnValue(
1088: getInstanceColumnRef(
1089: AbstractEditableObject.TAG_VERSION_COMMENT,
1090: bIsHist),
1091: m_sVersionComment);
1092: }
1093:
1094: m_dsi.execute(insert);
1095:
1096: } catch (DataStoreException e) {
1097: throw new ProfileException(
1098: "Error occurred processing insert", e);
1099: } catch (SQLException sql_e) {
1100: throw new ProfileException(
1101: "Error occurred processing insert", sql_e);
1102: } catch (DataAccessException e) {
1103: throw new ProfileException(
1104: "Error occurred processing insert", e);
1105: }
1106: }
1107:
1108: for (i = 0; i < m_values2Remove.size(); i++) {
1109: Object objVal = m_values2Remove.get(i);
1110:
1111: delete.clear();
1112:
1113: try {
1114: delete.setTable(sTable);
1115:
1116: delete.addWhereCondition(getColumnForData(), "=",
1117: getValueToStoreForValue(objVal));
1118:
1119: delete.addWhereCondition(this .getInstanceColumnRef(
1120: CLMN_PROFILE_ID, bIsHist), "=", prof
1121: .getId());
1122:
1123: m_dsi.execute(delete);
1124: } catch (DataStoreException e) {
1125: throw new ProfileException(
1126: "Error occurred processing insert", e);
1127: }
1128: }
1129: } else if (isPopulated() == false) {
1130: //if this happens to be a new property instance on the profile
1131: save(prof);
1132: }
1133:
1134: m_values2Add.clear();
1135: m_values2Remove.clear();
1136:
1137: m_bIsChanged = false;
1138: m_bIsPopulated = true;
1139:
1140: }
1141:
1142: /**
1143: * Sets this property instance as having changed since being populated
1144: * from the database.
1145: *
1146: * @param bIsChanged <code>true</code> if this property instance
1147: * has been changed, otherwise <code>false</code>
1148: */
1149: protected void setIsChanged(boolean bIsChanged) {
1150: m_bIsChanged = true;
1151: }
1152:
1153: /**
1154: * Returns <code>true</code> if this property instance has changed
1155: * since populated from the database, otherwise <code>false</code>
1156: * @return <code>true</code> if this property instance has changed
1157: * since populated from the database.
1158: */
1159: protected boolean isChanged() {
1160: return m_bIsChanged;
1161: }
1162:
1163: /**
1164: * Returns <code>true</code> if this property instance is historical,
1165: * otherwise <code>false</code>.
1166: *
1167: * @return <code>true</code> if this property instance is historical
1168: */
1169: protected boolean isHistorical() {
1170: return m_bIsHistorical;
1171: }
1172:
1173: /**
1174: * Sets this property instance as being historical.
1175: *
1176: * @param bIsHist <code>true</code> if this property insatnce is
1177: * to be historical
1178: */
1179: protected void setHistorical(boolean bIsHist) {
1180: m_bIsHistorical = bIsHist;
1181:
1182: }
1183:
1184: /**
1185: * Adds a value to this property instance.
1186: *
1187: * @param obj the value to add
1188: * @throws InvalidPropertyValueException if the value is invalid for
1189: * this property instance
1190: */
1191: protected void addValue(Object obj) throws PopulateException {
1192: if ((m_values.contains(obj) == false)) {
1193:
1194: if (m_logger.isLoggable(Level.FINE)) {
1195:
1196: try {
1197: StringBuffer sbuf = new StringBuffer();
1198:
1199: sbuf.append("adding value ").append(obj).append(
1200: " to property instance ").append(getName());
1201:
1202: AbstractProfiledObject profObj = getProfiledObject();
1203:
1204: if (profObj != null) {
1205: sbuf.append("for ").append(
1206: profObj.getClass().getName()).append(
1207: " ").append(profObj.getKey());
1208: }
1209:
1210: m_logger.logp(Level.FINE,
1211: this .getClass().getName(), "addValue", sbuf
1212: .toString());
1213: } catch (DataAccessException e) {
1214: m_logger.log(Level.WARNING,
1215: e.getLocalizedMessage(), e);
1216: }
1217: }
1218:
1219: // TODO: For a search, domain is not applicable
1220: // but there may be an occasion where we may
1221: // create a property instance without a profile.
1222: // So, in the save method we should do a check
1223: // if the value is valid or not.
1224: // probably this bit of code could move to the
1225: // save method.
1226: if (m_profile != null) {
1227: AbstractProfiledObject profObj = m_profile
1228: .getProfiledObject();
1229:
1230: try {
1231: Property prop = getProperty();
1232: Domain domain = prop.getDomain(profObj);
1233:
1234: if (domain != null) {
1235: int nMax = domain.getMaxOccurs();
1236:
1237: if (nMax > 0 && m_values.size() >= nMax) {
1238: String sPropName = prop.getName();
1239:
1240: if (m_logger.isLoggable(Level.INFO)) {
1241: StringBuffer sbuf = new StringBuffer();
1242:
1243: sbuf
1244: .append(
1245: "Have reached maximum number of values for this property - ")
1246: .append(sPropName).append(
1247: " on object ");
1248:
1249: if (profObj != null) {
1250: sbuf.append(
1251: profObj.getClass()
1252: .getName()).append(
1253: " key - ").append(
1254: profObj.getKey());
1255: }
1256:
1257: m_logger.logp(Level.INFO, this
1258: .getClass().getName(),
1259: "addValue", sbuf.toString());
1260:
1261: }
1262:
1263: throw new InvalidPropertyValueException(
1264: "Have reached maximum number of values for this property - "
1265: + sPropName);
1266: }
1267: }
1268: } catch (DataAccessException e) {
1269: if (m_logger.isLoggable(Level.WARNING)) {
1270: StringBuffer sbuf = new StringBuffer();
1271:
1272: sbuf
1273: .append(
1274: "Have reached maximum number of values for a property")
1275: .append(" on object ");
1276:
1277: if (profObj != null) {
1278: try {
1279: sbuf.append(
1280: profObj.getClass().getName())
1281: .append(" key - ").append(
1282: profObj.getKey());
1283: } catch (DataAccessException e1) {
1284: m_logger.log(Level.WARNING, e1
1285: .getLocalizedMessage(), e1);
1286: }
1287: }
1288:
1289: m_logger
1290: .logp(Level.WARNING, this .getClass()
1291: .getName(), "addValue", sbuf
1292: .toString());
1293:
1294: }
1295:
1296: throw new PopulateException(
1297: "Had problem getting domain for this profiled object - "
1298: + profObj.getClass().getName(), e);
1299: }
1300: }
1301:
1302: m_values.add(obj);
1303:
1304: if (m_bIsPopulated == true) {
1305:
1306: setIsChanged(true);
1307: m_values2Add.add(obj);
1308: }
1309: }
1310: }
1311:
1312: /**
1313: * Adds the given object as a value to this property instance with
1314: * the associated id.
1315: *
1316: * @param val the value to add
1317: * @param nId the id of the value
1318: *
1319: * @throws InvalidPropertyValueException if the value is invalid for
1320: * this property instance
1321: */
1322: protected void addValue(Object val, int nId)
1323: throws PopulateException {
1324: if (val != null) {
1325: if (m_values == null) {
1326: m_values = new Vector();
1327: }
1328:
1329: if (m_valueIds == null) {
1330: m_valueIds = new Vector();
1331: }
1332:
1333: addValue(val);
1334: m_valueIds.add(new Integer(nId));
1335: }
1336: }
1337:
1338: /**
1339: * Sets this object as having been populated.
1340: *
1341: * @param bIsPopulated <code>true</code> to indicate that this property
1342: * instance has been populated from the database
1343: */
1344: protected void setIsPopulated(boolean bIsPopulated) {
1345: m_bIsPopulated = bIsPopulated;
1346: m_bIsChanged = false;
1347: }
1348:
1349: /**
1350: * Returns the column reference for the data column for this property
1351: * instance.
1352: *
1353: * Utility method to ensure sub classes will implement the required
1354: * functionality to get column reference to the value column of the
1355: * database table
1356: *
1357: * @return the column reference for the data column for this property
1358: * instance.
1359: * @throws DataStoreException if an error occurs constructing the
1360: * column reference
1361: */
1362: abstract protected ColumnRef getColumnForData()
1363: throws DataStoreException;
1364:
1365: /**
1366: * Returns a representation of the specified value suitable
1367: * for saving to the database.
1368: *
1369: * @param val the value
1370: * @return a representation of the specified value suitable
1371: * for saving to the database.
1372: * @throws ProfileException if an error occurs transforming the value
1373: * to a representation suitable for the database
1374: */
1375: abstract protected Object getValueToStoreForValue(Object val)
1376: throws ProfileException;
1377:
1378: /*-------------------------------------------------------------------------------------
1379: Private Methods
1380: --------------------------------------------------------------------------------------*/
1381:
1382: /**
1383: * Sets the value ids of this property instance.
1384: *
1385: * Note that the ordering of the ids must match the ordering of their
1386: * associated values.
1387: *
1388: * @param values the list of value ids
1389: */
1390: private void setValueIds(List valueIds) {
1391: m_valueIds = new Vector();
1392:
1393: m_valueIds.addAll(valueIds);
1394: }
1395:
1396: /* (non-Javadoc)
1397: * @see org.openharmonise.rm.publishing.Publishable#getTagName()
1398: */
1399: public String getTagName() {
1400: return TAG_PROPERTYINSTANCE;
1401: }
1402:
1403: /**
1404: * Returns <code>true</code> if this property instance has been populated
1405: * from the database.
1406: *
1407: * @return <code>true</code> if this property instance has been populated
1408: * from the database
1409: */
1410: public boolean isPopulated() {
1411: return m_bIsPopulated;
1412: }
1413:
1414: /**
1415: * Sets the data store interface
1416: *
1417: * @param dsi the data store interface
1418: */
1419: public void setDataStoreInterface(AbstractDataStoreInterface dsi) {
1420: m_dsi = dsi;
1421:
1422: }
1423:
1424: /**
1425: * Returns the profiled object which this property instance is
1426: * assocated with.
1427: *
1428: * @return the profiled object which this property instance is
1429: * assocated with.
1430: */
1431: protected AbstractProfiledObject getProfiledObject() {
1432: AbstractProfiledObject profObj = null;
1433:
1434: if (m_profile != null) {
1435: profObj = m_profile.getProfiledObject();
1436: }
1437: return profObj;
1438: }
1439:
1440: /* (non-Javadoc)
1441: * @see org.openharmonise.rm.dsi.DataStoreObject#getId()
1442: */
1443: public int getId() {
1444: //returning dummy value as there is no id associated with a
1445: //property instance, method is needed to implement DataStoreeObject
1446: return -1;
1447: }
1448:
1449: }
|