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.resources;
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.SelectStatement;
0028: import org.openharmonise.rm.*;
0029: import org.openharmonise.rm.dsi.*;
0030: import org.openharmonise.rm.publishing.*;
0031: import org.openharmonise.rm.resources.publishing.Template;
0032: import org.w3c.dom.*;
0033:
0034: /**
0035: * The abstract class for the core objects of Harmonise, handling the very basic data handling for data such as
0036: * system identifiers, name and summary and incorporating core functionality to be built on
0037: * by implementing classes.
0038: *
0039: * @author Michael Bell
0040: * @version $Revision: 1.5 $
0041: *
0042: */
0043: public abstract class AbstractObject implements Cloneable, Comparable,
0044: Publishable, DataStoreObject, CacheableObject {
0045:
0046: // Constants
0047: /**
0048: * Constant for a unsaved identifier
0049: */
0050: public static final int NOTDBSAVED_ID = 0;
0051:
0052: /**
0053: * Constant for a unsaved object key
0054: */
0055: public static final int NOTDBSAVED_KEY = 0;
0056:
0057: //XML constants
0058: /**
0059: * HarmoniseObject XML tag name
0060: */
0061: public static final String TAG_HARMONISE_OBJECT = "HarmoniseObject";
0062:
0063: /**
0064: * Name tag name
0065: */
0066: public static final String TAG_NAME = "Name";
0067:
0068: /**
0069: * Summary tag name
0070: */
0071: public static final String TAG_SUMMARY = "Summary";
0072:
0073: /**
0074: * Link tag name
0075: */
0076: public static final String TAG_LINK = "Link";
0077:
0078: /**
0079: * Link text tag name
0080: */
0081: public static final String TAG_LINK_TEXT = "LinkText";
0082:
0083: /**
0084: * Link image tag name
0085: */
0086: public static final String TAG_LINK_IMAGE = "LinkImage";
0087:
0088: /**
0089: * Object key attribute name
0090: */
0091: public static final String ATTRIB_KEY = "key";
0092:
0093: /**
0094: * Object id attribute name
0095: */
0096: public static final String ATTRIB_ID = "id";
0097: /**
0098: * Object name attribute name
0099: */
0100: public static final String ATTRIB_NAME = "name";
0101:
0102: /**
0103: * Object type attribute name
0104: */
0105: public static final String ATTRIB_TYPE = "type";
0106:
0107: /**
0108: * Alternative object type attribute
0109: */
0110: public static final String ATTRIB_OBJECT_TYPE = "objectType";
0111:
0112: /**
0113: * Historical attribute
0114: */
0115: public static final String ATTRIB_HISTORICAL = "historical";
0116:
0117: //DB constants
0118: /**
0119: * Id database column name
0120: */
0121: protected static final String CLMN_ID = "id";
0122:
0123: /**
0124: * Object key databse column name
0125: */
0126: private static final String CLMN_KEY = "object_key";
0127:
0128: /**
0129: * Object name column name
0130: */
0131: protected static final String CLMN_NAME = "name";
0132:
0133: /**
0134: * Object summary column name
0135: */
0136: protected static final String CLMN_SUMMARY = "summary";
0137:
0138: /**
0139: * Object type column name
0140: */
0141: private static final String CLMN_TYPE = "type";
0142:
0143: /**
0144: * Historical extension for database table names
0145: */
0146: public static final String EXT_HIST = "_hist";
0147:
0148: // Attributes common to all objects
0149: /**
0150: * Object id
0151: */
0152: protected int m_nId = NOTDBSAVED_ID;
0153:
0154: /**
0155: * Object key
0156: */
0157: protected int m_nObjectKey = NOTDBSAVED_KEY;
0158:
0159: /**
0160: * Object name
0161: */
0162: protected String m_sName = "";
0163:
0164: /**
0165: * Object summary
0166: */
0167: protected String m_sSummary = "";
0168:
0169: /**
0170: * Historical flag, <code>true</code> if this is a historical object
0171: */
0172: private boolean m_bHistorical = false;
0173:
0174: /**
0175: * Type of object, will be the name of the class this should be
0176: * instantiated as by default
0177: */
0178: protected String m_sType;
0179:
0180: /**
0181: * Hashcode value for this object
0182: */
0183: private int m_nHashcode = 0;
0184:
0185: /**
0186: * Data store interface for this object
0187: */
0188: protected AbstractDataStoreInterface m_dsi = null;
0189:
0190: /**
0191: * Populated flag, <code>true</code> if this object has been populated
0192: * from the database
0193: */
0194: protected boolean m_bIsPopulated = false;
0195:
0196: /**
0197: * Changed flag, <code>true</code> if this object has been changed
0198: * (as compared to the data stored in the database)
0199: */
0200: protected boolean m_bIsChanged = false;
0201:
0202: /**
0203: * The database table containing this object's data
0204: */
0205: protected String m_sTable = null;
0206:
0207: /**
0208: * List of cache listeners
0209: */
0210: private List m_cache_listeners = new ArrayList();
0211:
0212: /**
0213: * Logger for this class
0214: */
0215: static private Logger m_logger = Logger
0216: .getLogger(AbstractObject.class.getName());
0217:
0218: //initialiser block
0219: {
0220: m_sType = this .getClass().getName();
0221: m_sTable = getDBTableName(); //ensure that table name is set
0222: }
0223:
0224: /**
0225: * Constructs a new or anonymous instance without an interface
0226: * to the database.
0227: */
0228: public AbstractObject() {
0229: super ();
0230: }
0231:
0232: /**
0233: * Standard constructor for a new or anonymous resource,
0234: * registering an <code>AbstractDataStoreInterface</code> to use
0235: * with all database communications.
0236: *
0237: * @param con the interface to the database
0238: */
0239: public AbstractObject(AbstractDataStoreInterface con) {
0240: m_dsi = con;
0241: }
0242:
0243: /**
0244: * Standard constructor for a new or anonymous historical resource,
0245: * registering an <code>AbstractDataStoreInterface</code> to use with all
0246: * database communications.
0247: *
0248: * @param con the interface to the database
0249: */
0250: public AbstractObject(AbstractDataStoreInterface con,
0251: boolean bIsHist) {
0252: this (con);
0253: m_bHistorical = bIsHist;
0254: }
0255:
0256: /**
0257: * Standard constructor for an existing resource,
0258: * registering an <code>AbstractDataStoreInterface</code> to use
0259: * with all database communications.
0260: *
0261: * @param con the interface to the database
0262: * @param nId the id of the resource
0263: */
0264: public AbstractObject(AbstractDataStoreInterface con, int nId) {
0265: this (con);
0266: m_nId = nId;
0267: }
0268:
0269: /**
0270: * Standard constructor for an existing resource which may be historical.
0271: *
0272: * @param con the interface to the database
0273: * @param nId the id of the resource
0274: * @param nKey the unique key of the resource
0275: * @param bIsHist <code>true</code> if the resource is historical
0276: */
0277: public AbstractObject(AbstractDataStoreInterface con, int nId,
0278: int nKey, boolean bIsHist) {
0279: this (con, nId);
0280: m_bHistorical = bIsHist;
0281: }
0282:
0283: /**
0284: * Returns <code>true</code> if this resource exists in the database.
0285: *
0286: * @return <code>true</code> if resource exists
0287: */
0288: public boolean exists() {
0289: boolean bIsExist = false;
0290: ResultSet rs = null;
0291: try {
0292: if (m_nId > NOTDBSAVED_ID || m_nObjectKey > NOTDBSAVED_KEY) {
0293: if (m_bIsPopulated == false) {
0294:
0295: SelectStatement select = new SelectStatement();
0296:
0297: ColumnRef refCol = null;
0298: int nRef = 0;
0299: if (m_bHistorical == false
0300: || isKeySupported() == false) {
0301: refCol = getInstanceColumnRef(ATTRIB_ID,
0302: m_bHistorical);
0303: nRef = m_nId;
0304: } else {
0305: refCol = getInstanceColumnRef(ATTRIB_KEY,
0306: m_bHistorical);
0307: nRef = m_nObjectKey;
0308: }
0309:
0310: select.addSelectColumn(refCol);
0311: select.addWhereCondition(refCol, "=", nRef);
0312:
0313: rs = m_dsi.execute(select);
0314:
0315: if (rs.next()) {
0316: bIsExist = true;
0317: }
0318: } else {
0319: bIsExist = true;
0320: }
0321: }
0322:
0323: } catch (DataStoreException e) {
0324: m_logger.log(Level.WARNING, e.getLocalizedMessage(), e);
0325: bIsExist = false;
0326: } catch (SQLException e) {
0327: m_logger.log(Level.WARNING, e.getLocalizedMessage(), e);
0328: bIsExist = false;
0329: } finally {
0330: if (rs != null) {
0331: try {
0332: rs.close();
0333: } catch (SQLException e) {
0334: m_logger.log(Level.WARNING,
0335: e.getLocalizedMessage(), e);
0336: }
0337: }
0338: }
0339:
0340: return bIsExist;
0341: }
0342:
0343: /**
0344: * Returns the object id.
0345: *
0346: * @return the id of the object
0347: */
0348: public int getId() {
0349: return m_nId;
0350: }
0351:
0352: /**
0353: * Sets the object's unique identifier key.
0354: *
0355: * @param nKey the object's unique identifier key
0356: */
0357: public void setKey(int nKey) {
0358: m_nObjectKey = nKey;
0359: }
0360:
0361: /**
0362: * Returns the unique identifier key
0363: *
0364: * @return the objects unique identifier key
0365: * @throws DataAccessException if an error occurs populating this object
0366: */
0367: public int getKey() throws DataAccessException {
0368: if ((m_nObjectKey == NOTDBSAVED_KEY)
0369: && (m_bIsPopulated == false)) {
0370: try {
0371: populateFromDatabase();
0372: } catch (PopulateException e) {
0373: throw new DataAccessException(e.getLocalizedMessage());
0374: }
0375:
0376: }
0377:
0378: return m_nObjectKey;
0379: }
0380:
0381: /**
0382: * Returns the object name.
0383: *
0384: * @return the name of the object
0385: * @throws DataAccessException if an error occurs populating this object
0386: */
0387: public String getName() throws DataAccessException {
0388: if ((m_sName == null || m_sName.length() == 0)
0389: && (m_bIsPopulated == false)) {
0390: try {
0391: populateFromDatabase();
0392: } catch (PopulateException e) {
0393: throw new DataAccessException("populate error", e);
0394: }
0395: }
0396:
0397: return m_sName;
0398: }
0399:
0400: /**
0401: * Sets the object name.
0402: *
0403: * @param sName the name of the object
0404: */
0405: public void setName(String sName) throws InvalidNameException {
0406: if (m_bIsPopulated) {
0407: if (m_sName.equals(sName) == false) {
0408: m_bIsChanged = true;
0409: }
0410: }
0411:
0412: m_sName = sName;
0413: }
0414:
0415: /**
0416: * Set the object summary.
0417: *
0418: * @param sSummary the object summary
0419: */
0420: public void setSummary(String sSummary) {
0421: if (sSummary == null) {
0422: sSummary = "";
0423: }
0424:
0425: if (m_bIsPopulated) {
0426: if ((m_sSummary == null && sSummary != null)
0427: || (m_sSummary.trim().equals(sSummary.trim()) == false)) {
0428: m_bIsChanged = true;
0429: }
0430: }
0431:
0432: m_sSummary = sSummary;
0433: }
0434:
0435: /**
0436: * Sets the data store interface for this object if this object does not already
0437: * have an interface to the data store.
0438: *
0439: * Note: This method is a convenience method for objects created with the empty
0440: * constructor, changing the data store interface on an existing object could lead
0441: * to unpredictable behaviour. At some point in the future we may support the switching of
0442: * datastores such that an object can be populated from one datastore and saved to
0443: * another but this is not the case at present.
0444: *
0445: * @param dsi the data store interface
0446: * @throws PopulateException if this object already has a data store interface
0447: */
0448: public void setDataStoreInterface(AbstractDataStoreInterface dsi)
0449: throws PopulateException {
0450: if (m_dsi != null && dsi.equals(m_dsi) == false) {
0451: throw new PopulateException(
0452: "Can't switch data store interfaces");
0453: }
0454:
0455: m_dsi = dsi;
0456: }
0457:
0458: /**
0459: * Returns the datastore interface associated with this object.
0460: *
0461: * @return the datastore interface associated with this object
0462: */
0463: public AbstractDataStoreInterface getDataStoreInterface() {
0464: return m_dsi;
0465: }
0466:
0467: /**
0468: * Returns the object summary.
0469: *
0470: * @return the object summary
0471: * @throws DataAccessException if an error occurs populating this object
0472: */
0473: public String getSummary() throws DataAccessException {
0474: if ((m_sSummary == null || m_sSummary.length() == 0)
0475: && !m_bIsPopulated) {
0476: try {
0477: populateFromDatabase();
0478: } catch (PopulateException e) {
0479: throw new DataAccessException(e.getLocalizedMessage());
0480: }
0481: }
0482:
0483: return m_sSummary;
0484: }
0485:
0486: /**
0487: * Sets the type (default class name) for this object.
0488: *
0489: * @param type the type for this object
0490: */
0491: public void setType(String type) {
0492:
0493: if (m_bIsPopulated) {
0494: if (m_sType.equals(type) == false) {
0495: m_bIsChanged = true;
0496: }
0497: }
0498:
0499: m_sType = type;
0500: }
0501:
0502: /**
0503: * Returns the object type (default class name) for this object.
0504: *
0505: * @return the object type (default class name) for this object
0506: * @throws DataAccessException if an error occurs populating this object
0507: */
0508: public String getType() throws DataAccessException {
0509: if ((m_sType.length() == 0) && (m_bIsPopulated == false)) {
0510: try {
0511: populateFromDatabase();
0512: } catch (PopulateException e) {
0513: throw new DataAccessException(e.getLocalizedMessage());
0514: }
0515: }
0516:
0517: return m_sType;
0518: }
0519:
0520: /**
0521: * Sets whether this object is historical.
0522: *
0523: * @param bIsHistorical The is historical flag
0524: */
0525: public void setHistorical(boolean bIsHistorical) {
0526: this .m_bHistorical = bIsHistorical;
0527: }
0528:
0529: /**
0530: * Returns <code>true</code> if this object is a historical object.
0531: *
0532: * @return <code>true</code> if this object is a historical object
0533: */
0534: public boolean isHistorical() {
0535: return m_bHistorical;
0536: }
0537:
0538: /**
0539: * Returns <code>true</code> if this object's data has been changed (as
0540: * compared to the data held in the database).
0541: *
0542: * @return <code>true</code> if this object's data has been changed
0543: * @throws DataAccessException if there is an error populating this object
0544: */
0545: public boolean isChanged() throws DataAccessException {
0546: if (m_bIsPopulated == false && m_bIsChanged == false) {
0547: try {
0548: populateFromDatabase();
0549: } catch (PopulateException e) {
0550: throw new DataAccessException("Data access error", e);
0551: }
0552: }
0553:
0554: return m_bIsChanged;
0555: }
0556:
0557: /**
0558: * Sets the object changed flag.
0559: *
0560: * @param bIsChanged <code>true</code> to set this object as having been changed
0561: */
0562: public void setIsChanged(boolean bIsChanged) {
0563: m_bIsChanged = bIsChanged;
0564: }
0565:
0566: /* (non-Javadoc)
0567: * @see java.lang.Object#equals(java.lang.Object)
0568: */
0569: public boolean equals(Object obj) {
0570: boolean bReturn = false;
0571:
0572: try {
0573: populateFromDatabase();
0574: } catch (PopulateException e) {
0575: //rather than throwing a RuntimeException(), I'm
0576: //electing to do nothing here - log it though
0577: m_logger.log(Level.WARNING, e.getLocalizedMessage(), e);
0578: }
0579:
0580: if (obj instanceof AbstractObject) {
0581:
0582: if (this == obj) {
0583: bReturn = true;
0584: } else if (obj.getClass().getName().equals(
0585: getClass().getName()) == true) {
0586: AbstractObject objCompare = (AbstractObject) obj;
0587:
0588: try {
0589: if ((this .getKey() != 0)
0590: && (objCompare.getKey() != 0)) {
0591: if (m_nObjectKey == objCompare.getKey()) {
0592: bReturn = true;
0593: }
0594: } else {
0595: try {
0596: if (m_sName.equals(objCompare.getName())) {
0597: bReturn = true;
0598: }
0599: } catch (Exception e) {
0600: }
0601: }
0602: } catch (Exception e) {
0603: throw new RuntimeException(e.getMessage());
0604: }
0605: }
0606:
0607: }
0608:
0609: return bReturn;
0610: }
0611:
0612: /* (non-Javadoc)
0613: * @see java.lang.Object#hashCode()
0614: */
0615: public int hashCode() {
0616:
0617: if (m_nHashcode == 0) {
0618: int nHash = 17;
0619:
0620: try {
0621: populateFromDatabase();
0622: } catch (PopulateException e) {
0623: //rather than throwing a RuntimeException(), I'm
0624: //electing to do nothing here - log it though
0625: m_logger.log(Level.WARNING, e.getLocalizedMessage(), e);
0626: }
0627:
0628: nHash = 37 * nHash + m_nId;
0629: nHash = 37 * nHash + m_nObjectKey;
0630: if (m_sName != null) {
0631: nHash = 37 * nHash + m_sName.hashCode();
0632: }
0633: if (m_sSummary != null) {
0634: nHash = 37 * nHash + m_sSummary.hashCode();
0635: }
0636: m_nHashcode = nHash;
0637: }
0638:
0639: return m_nHashcode;
0640: }
0641:
0642: /* (non-Javadoc)
0643: * @see org.openharmonise.rm.publishing.Publishable#populate(org.w3c.dom.Element, org.openharmonise.rm.publishing.State)
0644: */
0645: public void populate(Element xmlElement, State state)
0646: throws PopulateException {
0647: String sTagName = xmlElement.getTagName();
0648: Text txt = null;
0649: String sTemp = null;
0650:
0651: if (sTagName.equals(getTagName()) == true) {
0652: sTemp = xmlElement.getAttribute(ATTRIB_ID);
0653:
0654: if (!sTemp.equals("")) {
0655: m_nId = Integer.parseInt(sTemp);
0656: }
0657:
0658: NodeList nodes = xmlElement.getChildNodes();
0659: List postponedNodes = new Vector();
0660:
0661: for (int i = 0; i < nodes.getLength(); i++) {
0662: if (nodes.item(i).getNodeType() != Node.ELEMENT_NODE) {
0663: continue;
0664: }
0665:
0666: Element el = null;
0667: try {
0668: el = (Element) nodes.item(i);
0669: populate(el, state);
0670: } catch (PopulateDependencyException e) {
0671: postponedNodes.add(el);
0672: }
0673: }
0674: for (int i = 0; i < postponedNodes.size(); i++) {
0675: Element el = (Element) postponedNodes.get(i);
0676: populate(el, state);
0677: }
0678: } else if (sTagName.equals(TAG_NAME)) {
0679: txt = (Text) xmlElement.getFirstChild();
0680: try {
0681: setName(txt.getNodeValue());
0682: } catch (InvalidNameException e) {
0683: throw new PopulateException(e);
0684: }
0685: } else if (sTagName.equals(TAG_SUMMARY)) {
0686: txt = (Text) xmlElement.getFirstChild();
0687: setSummary(txt.getNodeValue());
0688: }
0689: }
0690:
0691: /* (non-Javadoc)
0692: * @see org.openharmonise.rm.publishing.Publishable#publish(org.openharmonise.rm.resources.publishing.Template, org.openharmonise.rm.publishing.HarmoniseOutput, org.openharmonise.rm.publishing.State)
0693: */
0694: public Element publish(Template template, HarmoniseOutput xmlDoc,
0695: State state) throws PublishException {
0696: Element resultEl = null;
0697:
0698: try {
0699:
0700: resultEl = publish(template.getTemplateRootElement(),
0701: xmlDoc, state);
0702: } catch (DataAccessException de) {
0703: throw new PublishException(de.getLocalizedMessage(), de);
0704: } catch (Exception e) {
0705: throw new PublishException(e.getLocalizedMessage(), e);
0706: }
0707:
0708: return resultEl;
0709: }
0710:
0711: /* (non-Javadoc)
0712: * @see org.openharmonise.rm.publishing.Publishable#publish(org.w3c.dom.Element, org.openharmonise.rm.publishing.HarmoniseOutput, org.openharmonise.rm.publishing.State)
0713: */
0714: public Element publish(Element topEl, HarmoniseOutput xmlDoc,
0715: State state) throws PublishException {
0716: Element docEl = null;
0717: NodeList nodes = null;
0718: Text txt = null;
0719: String sTagName = topEl.getTagName();
0720:
0721: try {
0722:
0723: if (topEl.getTagName().equals(getTagName())
0724: || topEl.getTagName().equals(TAG_HARMONISE_OBJECT)) {
0725: docEl = xmlDoc.createElement(getTagName());
0726:
0727: docEl.setAttribute(ATTRIB_ID, Integer.toString(m_nId));
0728:
0729: nodes = topEl.getChildNodes();
0730: } else if (sTagName.equals(TAG_NAME)) {
0731: docEl = xmlDoc.createElement(sTagName);
0732: txt = xmlDoc.createTextNode(getName());
0733: docEl.appendChild(txt);
0734: xmlDoc.copyChildren(docEl, topEl, new Vector());
0735: } else if (sTagName.equals(TAG_SUMMARY)) {
0736: docEl = xmlDoc.createElement(sTagName);
0737: txt = xmlDoc.createTextNode(getSummary());
0738: docEl.appendChild(txt);
0739: xmlDoc.copyChildren(docEl, topEl, new Vector());
0740: } else if (sTagName.equals(TAG_LINK)
0741: || sTagName.equals(TAG_LINK_TEXT)
0742: || sTagName.equals(TAG_LINK_IMAGE)) {
0743: // allow link text to only contain text, rather than a publishable object,
0744: // if it contains text copy this only.
0745: // note we check that it's a link_text tag, and that it has only *one*
0746: // child node (of type text), because <LinkText> <DocumentTitle/>
0747: // </LinkText> counts the whitespace as a child text node
0748: // that is <LinkText>Help</LinkText> works.
0749: Node childnode = topEl.getFirstChild();
0750:
0751: if ((sTagName.equals(TAG_LINK_TEXT) || sTagName
0752: .equals(TAG_LINK_IMAGE))
0753: && (childnode.getNodeType() == Node.TEXT_NODE)
0754: && ((topEl.getChildNodes()).getLength() == 1)) {
0755: docEl = (Element) xmlDoc.copyNode(topEl);
0756: } else {
0757: docEl = xmlDoc.createElement(sTagName);
0758:
0759: NodeList childnodes = topEl.getChildNodes();
0760:
0761: for (int k = 0; k < childnodes.getLength(); k++) {
0762: if (childnodes.item(k).getNodeType() == Node.ELEMENT_NODE) {
0763: Element tempEl = publish(
0764: (Element) childnodes.item(k),
0765: xmlDoc, state);
0766:
0767: if (tempEl != null) {
0768: docEl.appendChild(tempEl);
0769: }
0770: }
0771: }
0772: }
0773: }
0774:
0775: // recurse through the children if there are any
0776: Element formEl;
0777: Element el;
0778:
0779: if (nodes != null) {
0780: for (int i = 0; i < nodes.getLength(); i++) {
0781: if (nodes.item(i).getNodeType() != Node.ELEMENT_NODE) {
0782: continue;
0783: }
0784:
0785: formEl = (Element) nodes.item(i);
0786: el = publish(formEl, xmlDoc, state);
0787:
0788: if (el != null) {
0789: docEl.appendChild(el);
0790: }
0791: }
0792: }
0793:
0794: } catch (DataAccessException e) {
0795: throw new PublishException(e.getLocalizedMessage(), e);
0796: } catch (Exception ex) {
0797: throw new PublishException(ex.getLocalizedMessage(), ex);
0798: }
0799:
0800: return docEl;
0801: }
0802:
0803: /* (non-Javadoc)
0804: * @see org.openharmonise.rm.dsi.DataStoreObject#getInstanceColumnRef(java.lang.String, boolean)
0805: */
0806: public ColumnRef getInstanceColumnRef(String sColumn,
0807: boolean bIsHist) throws DataStoreException {
0808: String sDBTable = getTableName(bIsHist);
0809:
0810: return getObjectColumnRef(sDBTable, sColumn);
0811: }
0812:
0813: /**
0814: * Returns a column reference appropriate for the given
0815: * <code>AbstractObject</code> and column name.
0816: *
0817: * @param obj an <code>AbstractObject</code> object
0818: * @param sColumn the column, tag or attribute name
0819: * @return the column reference corresponding to the object and column
0820: * name given
0821: * @throws DataStoreException if an error occurs
0822: */
0823: public static ColumnRef getColumnRef(AbstractObject obj,
0824: String sColumn) throws DataStoreException {
0825: String sTable = obj.getDBTableName();
0826:
0827: return getObjectColumnRef(sTable, sColumn);
0828: }
0829:
0830: /**
0831: * Returns a column reference appropriate for the given
0832: * <code>AbstractObject</code> and column name.
0833: *
0834: * @param sClassname the class name of an <code>AbstractObject</code>
0835: * @param sColumn the column, tag or attribute name
0836: * @return the column reference corresponding to the class name and column
0837: * name given
0838: * @throws DataStoreException if an error occurs
0839: */
0840: public static ColumnRef getColumnRef(String sClassname,
0841: String sColumn) throws DataStoreException {
0842: return getColumnRef(sClassname, sColumn, false);
0843: }
0844:
0845: /**
0846: * Returns a column reference appropriate for the given
0847: * parameters.
0848: *
0849: * @param sClassname the class name of an <code>AbstractObject</code>
0850: * @param sColumn the column, tag or attribute name
0851: * @param bHist <code>true</code> if the column reference is
0852: * associated with a historical object
0853: * @return the column reference corresponding to the parameters
0854: * @throws DataStoreException if an error occurs
0855: */
0856: public static ColumnRef getColumnRef(String sClassname,
0857: String sColumn, boolean bHist) throws DataStoreException {
0858: ColumnRef returnColRef = null;
0859: String sTable = getTableName(sClassname, bHist);
0860:
0861: return getObjectColumnRef(sTable, sColumn);
0862: }
0863:
0864: /**
0865: * Returns the table name for the object of the class name given, taking account
0866: * for whether the historical table is needed or not.
0867: *
0868: * @param sClassname the class name of an <code>AbstractObject</code>
0869: * @param bHist <code>true</code> if the table name is
0870: * associated with a historical object
0871: * @return the table name for the object
0872: * @throws DataStoreException if an error occurs
0873: */
0874: public static String getTableName(String sClassname, boolean bHist)
0875: throws DataStoreException {
0876: String sTable = null;
0877:
0878: try {
0879: //check this class is an AbstractObject implementation
0880: Class clss = Class.forName(sClassname);
0881: Class absObjClass = AbstractObject.class;
0882: if (absObjClass.isAssignableFrom(clss) == false) {
0883: throw new DataStoreException(
0884: "Classname isn't AbstractObject");
0885: }
0886:
0887: sTable = DatabaseInfo.getInstance()
0888: .getTableName(sClassname);
0889:
0890: if (bHist == true) {
0891: sTable = sTable + EXT_HIST;
0892: }
0893: } catch (ClassNotFoundException e) {
0894: throw new DataStoreException(
0895: "Unable to get table name for class"
0896: + e.getLocalizedMessage());
0897: }
0898:
0899: return sTable;
0900: }
0901:
0902: /* (non-Javadoc)
0903: * @see java.lang.Object#clone()
0904: */
0905: public Object clone() {
0906: try {
0907: if (!m_bIsPopulated) {
0908: try {
0909: populateFromDatabase();
0910: } catch (PopulateException e) {
0911: throw new DataAccessException(e
0912: .getLocalizedMessage());
0913: }
0914: }
0915:
0916: AbstractObject other = (AbstractObject) super .clone();
0917:
0918: return other;
0919: } catch (CloneNotSupportedException e) {
0920: m_logger.log(Level.WARNING, e.getMessage(), e);
0921:
0922: return null;
0923: } catch (Exception e) {
0924: m_logger.log(Level.WARNING, e.getMessage(), e);
0925:
0926: return null;
0927: }
0928: }
0929:
0930: /**
0931: * Clears all data contained in object, the object does however
0932: * retain its id.
0933: *
0934: */
0935: public void clear() {
0936: m_nObjectKey = NOTDBSAVED_KEY;
0937: m_sName = "";
0938: m_sSummary = "";
0939:
0940: m_bHistorical = false;
0941: m_bIsPopulated = false;
0942: m_bIsChanged = false;
0943: }
0944:
0945: /* (non-Javadoc)
0946: * @see java.lang.Comparable#compareTo(java.lang.Object)
0947: */
0948: public int compareTo(Object obj) {
0949: int nCompare = 0;
0950: try {
0951: String sName = getName();
0952:
0953: AbstractObject abObj = (AbstractObject) obj;
0954:
0955: nCompare = sName.compareTo(abObj.getName());
0956: } catch (DataAccessException e) {
0957: m_logger.log(Level.WARNING, e.getLocalizedMessage(), e);
0958: }
0959:
0960: return nCompare;
0961: }
0962:
0963: /* (non-Javadoc)
0964: * @see org.openharmonise.rm.dsi.DataStoreObject#getDBTableName()
0965: */
0966: public abstract String getDBTableName();
0967:
0968: /*----------------------------------------------------------------------------
0969: Protected Functions
0970: -----------------------------------------------------------------------------*/
0971:
0972: /**
0973: * Returns a collection of column refs representing the object's core data
0974: * @return a collection of column refs representing the object's core data
0975: * @throws DataStoreException if an error occurs getting the column references
0976: */
0977: protected static Collection getCoreColumnRefs(String sClassname)
0978: throws DataStoreException {
0979: ArrayList list = new ArrayList();
0980:
0981: list.add(getColumnRef(sClassname, TAG_NAME));
0982: list.add(getColumnRef(sClassname, TAG_SUMMARY));
0983: list.add(getColumnRef(sClassname, ATTRIB_KEY));
0984:
0985: return list;
0986: }
0987:
0988: /**
0989: * Returns the historical table name for this object.
0990: *
0991: * @return the historical table name for this object
0992: */
0993: protected String getHistoricalDBTableName() {
0994: return m_sTable + EXT_HIST;
0995: }
0996:
0997: /**
0998: * Returns the database table name for an object of this object's type.
0999: *
1000: * @param bIsHist <code>true</code> if the historical table is needed
1001: * @return the database table name for an object of this object's type
1002: */
1003: protected String getTableName(boolean bIsHist) {
1004: String sTable = m_sTable;
1005:
1006: if (bIsHist == true) {
1007: sTable = m_sTable + EXT_HIST;
1008: }
1009:
1010: return sTable;
1011: }
1012:
1013: /**
1014: * Ensures that this object is fully poplated from the database.
1015: * @throws PopulateException
1016: */
1017: protected void fullPopulate() throws PopulateException {
1018: populateFromDatabase();
1019: }
1020:
1021: /**
1022: * Populates this object's core data from the database.
1023: *
1024: * @throws PopulateException if an error occurs getting data from database
1025: */
1026: protected synchronized void populateFromDatabase()
1027: throws PopulateException {
1028: if (m_nId != NOTDBSAVED_ID && m_bIsPopulated == false) {
1029:
1030: if (m_logger.isLoggable(Level.FINER)) {
1031: m_logger.logp(Level.FINER, this .getClass().getName(),
1032: "populateFromDatabase",
1033: "populating object, id - " + m_nId + ", key - "
1034: + m_nObjectKey);
1035: }
1036:
1037: if (isHistorical()
1038: && (m_nObjectKey == NOTDBSAVED_KEY && isKeySupported())) {
1039: throw new PopulateException(
1040: "Can't populate hisorical version without knowing object key");
1041: }
1042:
1043: ResultSet rs = null;
1044:
1045: try {
1046:
1047: SelectStatement select = new SelectStatement();
1048:
1049: addColumnsToPopulateQuery(select, m_bHistorical);
1050:
1051: if (m_bHistorical == false || isKeySupported() == false) {
1052: select.addWhereCondition(getInstanceColumnRef(
1053: ATTRIB_ID, m_bHistorical), "=", m_nId);
1054: } else {
1055: select.addWhereCondition(getInstanceColumnRef(
1056: ATTRIB_KEY, m_bHistorical), "=",
1057: m_nObjectKey);
1058: }
1059:
1060: rs = m_dsi.execute(select);
1061:
1062: boolean bResultFound = false;
1063: // flag to check if there were any results found
1064:
1065: while (rs.next() == true) {
1066: populateFromResultSetRow(rs, select);
1067:
1068: if (bResultFound == false) {
1069: bResultFound = true;
1070: }
1071: }
1072:
1073: if (bResultFound == false) {
1074: throw new ObjectNotFoundException(
1075: "Could not find a " + getClass().getName()
1076: + " object with id = " + getId());
1077:
1078: }
1079:
1080: m_bIsPopulated = true; // results were found
1081: } catch (DataStoreException ds_e) {
1082: throw new PopulateException(ds_e);
1083: } catch (SQLException sql_e) {
1084: throw new PopulateException(sql_e);
1085: } finally {
1086:
1087: if (rs != null) {
1088: try {
1089: rs.close();
1090: } catch (SQLException e) {
1091: throw new PopulateException(e
1092: .getLocalizedMessage());
1093: }
1094: }
1095: }
1096:
1097: } else {
1098: m_bIsPopulated = true;
1099: }
1100: }
1101:
1102: /**
1103: * Returns <code>true</code> if this object has a unique identifier key
1104: * as well as the identifier attribute.
1105: *
1106: * Note: This depends on how historical versions of this object are
1107: * handled. If all versions of the object need to have the same id, to
1108: * allow a consistent logical id, the key is used to identify versions.
1109: *
1110: * @return <code>true</code> if this object has a unique identifier key
1111: * as well as the identifier attribute
1112: */
1113: protected abstract boolean isKeySupported();
1114:
1115: /**
1116: * Adds columns to the select query for population of the object.
1117: *
1118: * Note: this method is used by <code>populateFromDatabase</code>
1119: * to construct its query.
1120: *
1121: * @param select the select statment to add columns to
1122: * @throws DataStoreException if an error occurs building the select statement
1123: */
1124: protected void addColumnsToPopulateQuery(SelectStatement select,
1125: boolean bIsHist) throws DataStoreException {
1126:
1127: try {
1128: ColumnRefCache cache = ColumnRefCache.getInstance();
1129:
1130: select.addSelectColumn(cache.getColumnRef(this , TAG_NAME,
1131: bIsHist));
1132: select.addSelectColumn(cache.getColumnRef(this ,
1133: TAG_SUMMARY, bIsHist));
1134: select.addSelectColumn(cache.getColumnRef(this ,
1135: ATTRIB_TYPE, bIsHist));
1136: select.addSelectColumn(cache.getColumnRef(this , ATTRIB_KEY,
1137: bIsHist));
1138:
1139: } catch (CacheException e) {
1140: throw new DataStoreException("Cache error", e);
1141: }
1142:
1143: }
1144:
1145: /**
1146: * Returns <code>true</code> if object has been populated.
1147: *
1148: * @return <code>true</code> if object has been populated
1149: */
1150: protected boolean isPopulated() {
1151: return m_bIsPopulated;
1152: }
1153:
1154: /**
1155: * Populates this object from the given result set.
1156: *
1157: * Note: this method must only handle data in the current row
1158: *
1159: * @param rs the result set
1160: * @param select the select statement used to generate the result set
1161: *
1162: * @throws PopulateException if an error occurs getting data from the result set
1163: */
1164: protected void populateFromResultSetRow(ResultSet rs,
1165: SelectStatement select) throws PopulateException {
1166: if (isPopulated() == false) {
1167: String sTemp = "";
1168:
1169: try {
1170: ColumnRefCache cache = ColumnRefCache.getInstance();
1171:
1172: ColumnRef nameCol = cache.getColumnRef(this , CLMN_NAME,
1173: m_bHistorical);
1174:
1175: if (select.containsSelectColumn(nameCol) == true) {
1176:
1177: sTemp = rs.getString(select
1178: .getResultSetIndex(nameCol));
1179:
1180: if ((sTemp != null) && (sTemp.length() > 0)) {
1181: if ((m_sName == null)
1182: || (m_sName.length() == 0)) {
1183: m_sName = sTemp;
1184: } else if (m_sName.equals(sTemp) == false) {
1185: m_bIsChanged = true;
1186: }
1187: }
1188: }
1189:
1190: ColumnRef summaryCol = cache.getColumnRef(this ,
1191: CLMN_SUMMARY, m_bHistorical);
1192: if (select.containsSelectColumn(summaryCol) == true) {
1193: sTemp = rs.getString(select
1194: .getResultSetIndex(summaryCol));
1195:
1196: if ((sTemp != null) && (sTemp.length() > 0)) {
1197: if ((m_sSummary == null)
1198: || (m_sSummary.length() == 0)) {
1199: m_sSummary = sTemp;
1200: } else if (m_sSummary.equals(sTemp) == false) {
1201: m_bIsChanged = true;
1202: }
1203: }
1204: }
1205:
1206: ColumnRef typeCol = cache.getColumnRef(this , CLMN_TYPE,
1207: m_bHistorical);
1208:
1209: if (select.containsSelectColumn(typeCol) == true) {
1210: sTemp = rs.getString(select
1211: .getResultSetIndex(typeCol));
1212:
1213: if ((sTemp != null) && (sTemp.length() > 0)) {
1214: setType(sTemp);
1215: }
1216: }
1217:
1218: ColumnRef keyCol = cache.getColumnRef(this , CLMN_KEY,
1219: m_bHistorical);
1220: if (select.containsSelectColumn(keyCol) == true) {
1221: int nKey = rs.getInt(select
1222: .getResultSetIndex(keyCol));
1223:
1224: if (nKey > 0) {
1225: setKey(nKey);
1226: }
1227: }
1228:
1229: } catch (SQLException e) {
1230: try {
1231: System.out.println("!!"
1232: + m_dsi.getSelectStatement(select));
1233: } catch (DataStoreException e1) {
1234: m_logger.log(Level.WARNING,
1235: e.getLocalizedMessage(), e1);
1236: }
1237:
1238: throw new PopulateException("SQL error", e);
1239: } catch (CacheException e) {
1240: throw new PopulateException(
1241: "Cache error getting col ref", e);
1242: }
1243: }
1244: }
1245:
1246: /**
1247: * Marks an object as a new object, correctly setting all of the
1248: * appropriate flags.
1249: *
1250: * @throws PopulateException if an error occurs
1251: */
1252: protected void markAsNew() throws PopulateException {
1253: m_nId = NOTDBSAVED_ID;
1254: m_bIsPopulated = true;
1255: m_bIsChanged = true;
1256: }
1257:
1258: /**
1259: * Set method for id, here for completeness, should use constructor.
1260: *
1261: * Note: Once an object's id has been assigned within the framework it can not
1262: * be reassigned. This method should only be used in conjunction with the empty
1263: * constructor to create an instance of an object with a specific identifier.
1264: *
1265: * @param nId The id of the object
1266: */
1267: public void setId(int nId) {
1268: if (isPopulated() == true) {
1269: if (m_nId != NOTDBSAVED_ID && nId != m_nId) {
1270: m_bIsChanged = true;
1271: }
1272: }
1273:
1274: m_nId = nId;
1275: }
1276:
1277: /**
1278: * Returns a column reference of the correct type given the table and column
1279: * identifier. The column identifier may be an XML tag.
1280: *
1281: * @param sDBTable the database table
1282: * @param sColumn the column, tag or attribute name
1283: * @return the appropriate column reference
1284: * @throws DataStoreException if an error occurs
1285: * @throws InvalidColumnReferenceException if the given column name is
1286: * invalid
1287: */
1288: protected static ColumnRef getObjectColumnRef(String sDBTable,
1289: String sColumn) throws DataStoreException {
1290: ColumnRef returnColRef = null;
1291:
1292: if (sColumn.equals(TAG_NAME) == true
1293: || sColumn.equals(CLMN_NAME) == true) {
1294: returnColRef = new ColumnRef(sDBTable, CLMN_NAME,
1295: ColumnRef.TEXT);
1296: } else if (sColumn.equals(TAG_SUMMARY) == true
1297: || sColumn.equals(CLMN_SUMMARY) == true) {
1298: returnColRef = new ColumnRef(sDBTable, CLMN_SUMMARY,
1299: ColumnRef.TEXT);
1300: } else if (sColumn.equals(ATTRIB_KEY) == true
1301: || sColumn.equals(CLMN_KEY) == true) {
1302: returnColRef = new ColumnRef(sDBTable, CLMN_KEY,
1303: ColumnRef.NUMBER);
1304: } else if (sColumn.equals(ATTRIB_ID) == true
1305: || sColumn.equals(CLMN_ID) == true) {
1306: returnColRef = new ColumnRef(sDBTable, CLMN_ID,
1307: ColumnRef.NUMBER);
1308: } else if (sColumn.equals(ATTRIB_OBJECT_TYPE) == true
1309: || sColumn.equals(ATTRIB_TYPE) == true
1310: || sColumn.equals(CLMN_TYPE) == true) {
1311: returnColRef = new ColumnRef(sDBTable, CLMN_TYPE,
1312: ColumnRef.TEXT);
1313: }
1314:
1315: if (returnColRef == null) {
1316: throw new InvalidColumnReferenceException(sColumn);
1317: }
1318:
1319: return returnColRef;
1320: }
1321:
1322: /* (non-Javadoc)
1323: * @see org.openharmonise.commons.cache.CacheableObject#addCacheListener(org.openharmonise.commons.cache.CacheListener)
1324: */
1325: public void addCacheListener(CacheListener listener) {
1326: m_cache_listeners.add(listener);
1327:
1328: }
1329:
1330: /* (non-Javadoc)
1331: * @see org.openharmonise.commons.cache.CacheableObject#getCacheListeners()
1332: */
1333: public List getCacheListeners() {
1334: return m_cache_listeners;
1335: }
1336:
1337: /* (non-Javadoc)
1338: * @see org.openharmonise.commons.cache.CacheableObject#removeCacheListener(org.openharmonise.commons.cache.CacheListener)
1339: */
1340: public void removeCacheListener(CacheListener listener) {
1341: m_cache_listeners.remove(listener);
1342:
1343: }
1344:
1345: /* (non-Javadoc)
1346: * @see org.openharmonise.commons.cache.CacheableObject#notifyCacheListeners()
1347: */
1348: public void notifyCacheListeners() {
1349: for (Iterator iter = m_cache_listeners.iterator(); iter
1350: .hasNext();) {
1351: CacheListener listener = (CacheListener) iter.next();
1352: listener.objectRemovedFromCache(this);
1353: }
1354:
1355: }
1356: }
|