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.*;
0028: import org.openharmonise.rm.*;
0029: import org.openharmonise.rm.dsi.*;
0030: import org.openharmonise.rm.factory.*;
0031: import org.openharmonise.rm.publishing.*;
0032: import org.openharmonise.rm.resources.lifecycle.*;
0033: import org.openharmonise.rm.resources.publishing.*;
0034: import org.openharmonise.rm.search.Search;
0035: import org.w3c.dom.*;
0036:
0037: /**
0038: * Abstract class that provides the necessary functionality for objects
0039: * which can be parents in a parent-child relationship within Harmonise.
0040: * Children are indexed so that the order of children can be managed.
0041: *
0042: * @author Michael Bell
0043: * @version $Revision: 1.6.2.1 $
0044: */
0045: public abstract class AbstractParentObject extends AbstractChildObject
0046: implements Editable, Publishable, DataStoreObject, Cloneable,
0047: EditEventListener {
0048:
0049: //DB constant
0050: /**
0051: * Relationship table position column name
0052: */
0053: private static final String CLMN_POSITION = "pos";
0054:
0055: //Constants used for accessing children by type
0056: /**
0057: * Constant for branch node type
0058: */
0059: public static final int BRANCH_NODES = 1;
0060:
0061: /**
0062: * Constant for all node types
0063: */
0064: public static final int LEAF_NODES = 2;
0065:
0066: /**
0067: * Constant for leaf node types
0068: */
0069: public static final int ALL_NODES = 3;
0070:
0071: //XML constants
0072: /**
0073: * Children tag name
0074: */
0075: public static final String TAG_CHILDREN = "Children";
0076:
0077: /**
0078: * Contents tag name
0079: */
0080: public static final String TAG_CONTENTS = "Contents";
0081:
0082: /**
0083: * Sub-groups tag name
0084: */
0085: public static final String TAG_SUBGROUPS = "SubGroups";
0086:
0087: /**
0088: * Attach tag name
0089: */
0090: public static final String TAG_ATTACH = "Attach";
0091:
0092: /**
0093: * Detach tag name
0094: */
0095: public final String TAG_DETACH = "Detach";
0096:
0097: //Member variables
0098: /**
0099: * List of <code>OrderableCachePointer</code> objects
0100: * referencing the children of this object
0101: */
0102: protected List m_children = null;
0103:
0104: /**
0105: * The maximum value of index for children of this object
0106: */
0107: protected int m_nMaxIndex = -1;
0108:
0109: /**
0110: * List of <code>OrderableCachePointer</code> objects
0111: * referencing the children to be added on the
0112: * next <code>save()</code> operation
0113: */
0114: protected List m_add_children = null;
0115:
0116: /**
0117: * List of <code>OrderableCachePointer</code> objects
0118: * referencing the children to be removed on the next
0119: * <code>save()</code> operation
0120: */
0121: protected List m_remove_children = null;
0122:
0123: /**
0124: * List of ids of children of those to be added for which this object is the 'real' parent
0125: */
0126: protected List m_add_real_children = null;
0127:
0128: /**
0129: * List of ids of children of this object for which this object is the 'real' parent
0130: */
0131: protected List m_real_children = null;
0132:
0133: /**
0134: * <code>boolean</code> flag which indicates whether the list of
0135: * children of this object has been populated
0136: */
0137: protected boolean m_bIsChildrenPopulated = false;
0138:
0139: /**
0140: * List of archived children
0141: */
0142: protected List m_archivedChildren = null;
0143:
0144: /**
0145: * List of archived descendants
0146: */
0147: protected List m_allArchivedChildren = null;
0148:
0149: /**
0150: * <code>boolean</code> flag which indicates whether the list of
0151: * children of this object is to be changed on the next
0152: * <code>save()</code> operation
0153: */
0154: protected boolean m_bIsContentsChanged = false;
0155:
0156: /**
0157: * <code>boolean</code> flag which indicates whether the list of
0158: * archived children has been populated
0159: */
0160: protected boolean m_bIsArchivedChildrenPopulated = false;
0161:
0162: /**
0163: * <code>boolean</code> flag which indicates whether the list
0164: * of all archived descendants has been populated
0165: */
0166: protected boolean m_bIsAllArchivedChildrenPopulated = false;
0167:
0168: /**
0169: * Logger for this class
0170: */
0171: private static Logger m_logger = Logger
0172: .getLogger(AbstractParentObject.class.getName());
0173:
0174: //initialiser block
0175: {
0176: m_children = new Vector();
0177: m_add_children = new Vector();
0178: m_remove_children = new Vector();
0179: m_add_real_children = new Vector();
0180: m_real_children = new Vector();
0181: m_archivedChildren = new Vector();
0182: m_allArchivedChildren = new Vector();
0183: }
0184:
0185: /**
0186: * Constructs a new or anonymous instance without an interface
0187: * to the database.
0188: */
0189: public AbstractParentObject() {
0190: super ();
0191: }
0192:
0193: /**
0194: * Constructor for a new or anonymous instance.
0195: *
0196: * @param dbintrf the data store interface
0197: */
0198: public AbstractParentObject(AbstractDataStoreInterface dbintrf) {
0199: super (dbintrf);
0200: }
0201:
0202: /**
0203: * Constructs an existing resource which may be historical.
0204: *
0205: * @param dbintrf the interface to the database
0206: * @param nId the id of the resource
0207: * @param nKey the unique key of the resource
0208: * @param bIsHist <code>true</code> if the resource is historical
0209: */
0210: public AbstractParentObject(AbstractDataStoreInterface dbintrf,
0211: int nId, int nKey, boolean bIsHist) {
0212: super (dbintrf, nId, nKey, bIsHist);
0213: }
0214:
0215: /**
0216: * Standard constructor for an existing resource,
0217: * registering an <code>AbstractDataStoreInterface</code> to use
0218: * with all database communications.
0219: *
0220: * @param dbintrf the interface to the database
0221: * @param nId the id of the resource
0222: */
0223: public AbstractParentObject(AbstractDataStoreInterface dbintrf,
0224: int nId) {
0225: super (dbintrf, nId);
0226: }
0227:
0228: /* (non-Javadoc)
0229: * @see org.openharmonise.rm.resources.AbstractChildObject#getPath()
0230: */
0231: public String getPath() throws DataAccessException {
0232: String sPath = null;
0233:
0234: AbstractParentObject parent = getRealParent();
0235:
0236: //if it has no real parent return the root slash
0237: if (parent == null) {
0238: sPath = separator;
0239: } else {
0240: sPath = super .getPath();
0241: }
0242:
0243: return sPath;
0244: }
0245:
0246: /* (non-Javadoc)
0247: * @see org.openharmonise.rm.resources.AbstractObject#markAsNew()
0248: */
0249: public void markAsNew() throws PopulateException {
0250: super .markAsNew();
0251: m_bIsContentsChanged = false;
0252:
0253: clearChildren();
0254: clearArchivedChildren();
0255: }
0256:
0257: /* (non-Javadoc)
0258: * @see java.lang.Object#clone()
0259: */
0260: public Object clone() {
0261: AbstractParentObject other = null;
0262:
0263: try {
0264: if (m_bIsChildrenPopulated == false) {
0265: populateChildrenFromDatabase();
0266: }
0267:
0268: other = (AbstractParentObject) super .clone();
0269:
0270: //the Lists below are cloned in the 'set' methods
0271: if (m_children != null) {
0272: other.setChildren(m_children);
0273: }
0274:
0275: } catch (InvalidChildException e) {
0276: throw new IllegalStateException(
0277: "Problem occured adding child to clone:"
0278: + e.getLocalizedMessage());
0279: } catch (PopulateException e) {
0280: throw new IllegalStateException(
0281: "Problem occured populating:"
0282: + e.getLocalizedMessage());
0283: }
0284:
0285: return other;
0286: }
0287:
0288: /**
0289: * Clears all stored lists of children.
0290: *
0291: */
0292: public void clearChildren() {
0293: m_bIsChildrenPopulated = false;
0294: m_children = null;
0295: m_real_children = null;
0296: }
0297:
0298: /* (non-Javadoc)
0299: * @see org.openharmonise.rm.resources.AbstractObject#clear()
0300: */
0301: public void clear() {
0302: super .clear();
0303: m_bIsContentsChanged = false;
0304:
0305: clearChildren();
0306: clearArchivedChildren();
0307: }
0308:
0309: /* (non-Javadoc)
0310: * @see org.openharmonise.rm.publishing.Publishable#publish(org.w3c.dom.Element, org.openharmonise.rm.publishing.HarmoniseOutput, org.openharmonise.rm.publishing.State)
0311: */
0312: public org.w3c.dom.Element publish(Element topEl,
0313: HarmoniseOutput xmlDoc, State state)
0314: throws PublishException {
0315: Element docEl = null;
0316: NodeList nodes = null;
0317: Text txt = null;
0318: String sTagName = topEl.getTagName();
0319:
0320: if (sTagName.equals(TAG_CONTENTS)) {
0321: docEl = xmlDoc.createElement(TAG_CONTENTS);
0322: nodes = topEl.getChildNodes();
0323: } else if (sTagName.equals(TAG_SUBGROUPS)) {
0324: docEl = xmlDoc.createElement(TAG_SUBGROUPS);
0325:
0326: NodeList nlChildren = topEl.getChildNodes();
0327: Node nTemp = null;
0328:
0329: for (int j = 0; j < nlChildren.getLength(); j++) {
0330: nTemp = nlChildren.item(j);
0331:
0332: if (nTemp.getNodeType() != Node.ELEMENT_NODE) {
0333: continue;
0334: } else if (nTemp.getNodeName().equals(
0335: Template.TAG_TEMPLATE)) {
0336: List subGroups = null;
0337: try {
0338: subGroups = this
0339: .getChildrenByType(BRANCH_NODES);
0340: } catch (DataAccessException e) {
0341: throw new PublishException(
0342: "Error occured getting branch children",
0343: e);
0344: }
0345:
0346: int nPageId = -1;
0347:
0348: NodeList pageNodes = ((Element) nTemp)
0349: .getElementsByTagName(WebPage.TAG_PAGE);
0350:
0351: if (pageNodes.getLength() > 0) {
0352: nPageId = Integer
0353: .parseInt(((Element) pageNodes.item(0))
0354: .getAttribute(AbstractObject.ATTRIB_ID));
0355: }
0356:
0357: if (subGroups.size() > 0) {
0358: Template grpTemplate = null;
0359: try {
0360: grpTemplate = (Template) HarmoniseObjectFactory
0361: .instantiateHarmoniseObject(
0362: m_dsi,
0363: Template.class.getName(),
0364: Integer
0365: .parseInt(((Element) nTemp)
0366: .getAttribute(AbstractObject.ATTRIB_ID)));
0367: } catch (NumberFormatException e) {
0368: throw new PublishException(e);
0369: } catch (HarmoniseFactoryException e) {
0370: throw new PublishException(e);
0371: }
0372:
0373: if (grpTemplate == null) {
0374: throw new PublishException(
0375: "No template supplied.");
0376: }
0377:
0378: String sSubClassName = null;
0379:
0380: NodeList tmpNL = nTemp.getChildNodes();
0381:
0382: if (tmpNL.getLength() > 0) {
0383: boolean bFound = false;
0384: int k = 0;
0385:
0386: while ((bFound == false)
0387: && (k < tmpNL.getLength())) {
0388: if (tmpNL.item(k).getNodeType() == Node.ELEMENT_NODE) {
0389: Element tmpEl = (Element) tmpNL
0390: .item(k);
0391:
0392: if (tmpEl.getTagName().equals(
0393: WebPage.TAG_PAGE) == false) {
0394: bFound = true;
0395: try {
0396: sSubClassName = HarmoniseObjectFactory
0397: .getClassName(
0398: this .m_dsi,
0399: tmpEl);
0400: } catch (HarmoniseFactoryException e) {
0401: throw new PublishException(
0402: "Error occured getting data from factory",
0403: e);
0404: }
0405: }
0406: }
0407:
0408: k++;
0409: }
0410: }
0411:
0412: if (sSubClassName == null) {
0413: try {
0414: sSubClassName = HarmoniseObjectFactory
0415: .getClassName(
0416: this .m_dsi,
0417: grpTemplate
0418: .getTemplateRootElement());
0419: } catch (HarmoniseFactoryException e) {
0420: throw new PublishException(
0421: "Error occured getting data from factory",
0422: e);
0423: } catch (DataAccessException e) {
0424: throw new PublishException(
0425: "Error occured getting data from Template",
0426: e);
0427: }
0428: }
0429:
0430: try {
0431: Class objClass = Class
0432: .forName(sSubClassName);
0433:
0434: List matchedSubs = getChildrenByClass(objClass);
0435:
0436: Iterator iter = matchedSubs.iterator();
0437:
0438: while (iter.hasNext()) {
0439: Publishable pObj = (Publishable) iter
0440: .next();
0441:
0442: boolean bPublishable = true;
0443:
0444: PublishFilter filter = state
0445: .getPublishFilter();
0446: if (filter != null) {
0447:
0448: bPublishable = filter.allowPublish(
0449: this , pObj);
0450: }
0451:
0452: if (bPublishable == true) {
0453:
0454: Element grpEl = pObj.publish(
0455: grpTemplate, xmlDoc, state);
0456:
0457: if (nPageId > 0) {
0458: xmlDoc
0459: .addPageIdToLinkNode(
0460: state
0461: .getLoggedInUser(),
0462: grpEl, nPageId);
0463: }
0464:
0465: docEl.appendChild(grpEl);
0466: }
0467: }
0468: } catch (DataAccessException e) {
0469: throw new PublishException(
0470: "Error occured getting data from Template",
0471: e);
0472: } catch (DOMException e) {
0473: throw new PublishException(
0474: "DOMException occured", e);
0475: } catch (ClassNotFoundException e) {
0476: throw new PublishException(
0477: "Class not found", e);
0478: }
0479: }
0480: }
0481: }
0482: } else if (sTagName.equals(TAG_CHILDREN)) {
0483: docEl = xmlDoc.createElement(TAG_CHILDREN);
0484:
0485: NodeList nlChildren = topEl.getChildNodes();
0486: Node nTemp = null;
0487: boolean bHistorical = false;
0488: String sHistorical = topEl.getAttribute(ATTRIB_HISTORICAL);
0489:
0490: if (sHistorical != null) {
0491: if (sHistorical.equals("1")) {
0492: bHistorical = true;
0493: }
0494: }
0495:
0496: for (int j = 0; j < nlChildren.getLength(); j++) {
0497: nTemp = nlChildren.item(j);
0498:
0499: if (nTemp.getNodeType() != Node.ELEMENT_NODE) {
0500: continue;
0501: } else if (nTemp.getNodeName().equals(
0502: Template.TAG_TEMPLATE)) {
0503: List children = null;
0504:
0505: try {
0506: if (bHistorical) {
0507: children = this
0508: .getArchivedChildrenByType(LEAF_NODES);
0509: } else {
0510: children = this
0511: .getChildrenByType(LEAF_NODES);
0512: }
0513: } catch (DataAccessException e) {
0514: throw new PublishException(
0515: "Error occured accessing children", e);
0516: }
0517:
0518: if (children.size() > 0) {
0519: Template template = null;
0520: try {
0521: template = (Template) HarmoniseObjectFactory
0522: .instantiateHarmoniseObject(
0523: m_dsi,
0524: Template.class.getName(),
0525: Integer
0526: .parseInt(((Element) nTemp)
0527: .getAttribute(AbstractObject.ATTRIB_ID)));
0528: } catch (NumberFormatException e) {
0529: throw new PublishException(e);
0530: } catch (HarmoniseFactoryException e) {
0531: throw new PublishException(e);
0532: }
0533:
0534: int nPageId = -1;
0535:
0536: NodeList pageNodes = ((Element) nTemp)
0537: .getElementsByTagName(WebPage.TAG_PAGE);
0538:
0539: if (pageNodes.getLength() > 0) {
0540: nPageId = Integer
0541: .parseInt(((Element) pageNodes
0542: .item(0))
0543: .getAttribute(AbstractObject.ATTRIB_ID));
0544: }
0545:
0546: if (template == null) {
0547: throw new PublishException(
0548: "No template supplied.");
0549: }
0550:
0551: Iterator iter = children.iterator();
0552: while (iter.hasNext()) {
0553: Publishable pObj = (Publishable) iter
0554: .next();
0555: Element chldEl = pObj.publish(template,
0556: xmlDoc, state);
0557:
0558: if (nPageId > 0) {
0559: xmlDoc.addPageIdToLinkNode(state
0560: .getLoggedInUser(), chldEl,
0561: nPageId);
0562: }
0563:
0564: docEl.appendChild(chldEl);
0565: }
0566: }
0567: }
0568: }
0569: } else if (sTagName.equals(Search.TAG_LIST)) {
0570: Search searchObj;
0571: try {
0572: searchObj = (Search) HarmoniseObjectFactory
0573: .instantiatePublishableObject(m_dsi, topEl,
0574: state);
0575: searchObj.addConditionGroup(this );
0576:
0577: docEl = searchObj.publish(topEl, xmlDoc, state);
0578: } catch (HarmoniseFactoryException e) {
0579: throw new PublishException(
0580: "Error occured processing List element", e);
0581: }
0582:
0583: } else {
0584: // if we don't know about the tag, pass it up
0585: docEl = super .publish(topEl, xmlDoc, state);
0586: }
0587:
0588: // recurse through the children if there are any
0589: Element formEl;
0590: Element el;
0591:
0592: if (nodes != null) {
0593: for (int i = 0; i < nodes.getLength(); i++) {
0594: if (nodes.item(i).getNodeType() != Node.ELEMENT_NODE) {
0595: continue;
0596: }
0597:
0598: formEl = (Element) nodes.item(i);
0599: el = publish(formEl, xmlDoc, state);
0600:
0601: if (el != null) {
0602: try {
0603: docEl.appendChild(el);
0604: } catch (org.w3c.dom.DOMException e) {
0605: throw new PublishException(el.getTagName()
0606: + ":" + e.getMessage());
0607: }
0608: }
0609: }
0610: }
0611:
0612: return docEl;
0613: }
0614:
0615: /* (non-Javadoc)
0616: * @see org.openharmonise.rm.publishing.Publishable#populate(org.w3c.dom.Element, org.openharmonise.rm.publishing.State)
0617: */
0618: public void populate(Element xmlElement, State state)
0619: throws PopulateException {
0620: Element formEl = xmlElement;
0621: String sTagName = formEl.getTagName();
0622: int nId = -1;
0623:
0624: if (sTagName.equals(TAG_CHILDREN)) {
0625: String sIdTemp = "";
0626: String sDefault = "";
0627: boolean bDefault = false;
0628:
0629: NodeList nlAttach = formEl.getElementsByTagName(TAG_ATTACH);
0630:
0631: if (nlAttach.getLength() > 0) {
0632: NodeList nlAttachObjects = ((Element) nlAttach.item(0))
0633: .getChildNodes();
0634:
0635: for (int j = 0; j < nlAttachObjects.getLength(); j++) {
0636: if (nlAttachObjects.item(j).getNodeType() != Node.ELEMENT_NODE) {
0637: continue;
0638: }
0639:
0640: sIdTemp = ((Element) nlAttachObjects.item(j))
0641: .getAttribute(ATTRIB_ID);
0642: sDefault = ((Element) nlAttachObjects.item(j))
0643: .getAttribute(ATTRIB_DEFAULT);
0644:
0645: if (!sIdTemp.equals("XXXX")) {
0646:
0647: if ((sDefault != null) && sDefault.equals("1")) {
0648: bDefault = true;
0649: } else {
0650: bDefault = false;
0651: }
0652:
0653: try {
0654: addChild(
0655: (AbstractChildObject) this
0656: .getObjectFromFactory((Element) nlAttachObjects
0657: .item(j)), bDefault);
0658: } catch (InvalidChildException e) {
0659: throw new PopulateException(
0660: "Invalid child:"
0661: + e.getLocalizedMessage());
0662: } catch (HarmoniseFactoryException e) {
0663: throw new PopulateException(
0664: "Invalid child:"
0665: + e.getLocalizedMessage());
0666: }
0667: }
0668: }
0669: }
0670:
0671: NodeList nlDetach = formEl.getElementsByTagName(TAG_DETACH);
0672:
0673: if (nlDetach.getLength() > 0) {
0674: NodeList nlDetachObjects = ((Element) nlDetach.item(0))
0675: .getChildNodes();
0676:
0677: for (int j = 0; j < nlDetachObjects.getLength(); j++) {
0678: if (nlDetachObjects.item(j).getNodeType() != Node.ELEMENT_NODE) {
0679: continue;
0680: }
0681:
0682: sIdTemp = ((Element) nlDetachObjects.item(j))
0683: .getAttribute(ATTRIB_ID);
0684:
0685: if (!sIdTemp.equals("XXXX")) {
0686:
0687: try {
0688: removeChild((AbstractChildObject) this
0689: .getObjectFromFactory((Element) nlDetachObjects
0690: .item(j)));
0691: } catch (HarmoniseFactoryException e) {
0692: throw new PopulateException(
0693: "Error occured removing child from parent",
0694: e);
0695: }
0696: }
0697: }
0698: }
0699: } else {
0700: super .populate(formEl, state);
0701: }
0702: }
0703:
0704: /**
0705: * Adds the specified object as a child to this parent.
0706: *
0707: * @param obj the child to be added
0708: * @throws InvalidChildException if the child is an invalid child for this parent
0709: * @throws PopulateException if there is an error populating the children of this object
0710: */
0711: public void addChild(AbstractChildObject obj)
0712: throws InvalidChildException, PopulateException {
0713: addChild(-1, obj, false);
0714: }
0715:
0716: /**
0717: * Inserts the specified child object at the specified position
0718: * in the list of children. Shifts the object currently at that
0719: * position (if any) and any subsequent objects to the
0720: * right (adds one to their indices).
0721: *
0722: * @param nIndex the desired index for the specified child
0723: * @param obj the child to be added
0724: * @throws InvalidChildException if the child is an invalid child for this parent
0725: * @throws PopulateException if there is an error populating the children of this object
0726: */
0727: public void addChild(int nIndex, AbstractChildObject obj)
0728: throws InvalidChildException, PopulateException {
0729: addChild(nIndex, obj, false);
0730: }
0731:
0732: /**
0733: * Adds the specified object as a child to this parent. If
0734: * <code>bIsSoftLink</code> is true then the relationship is
0735: * weak, meaning this group is not the main group of the
0736: * child.
0737: *
0738: * @param obj the new child
0739: * @param bIsSoftLink <code>true</code> if the relationship is weak
0740: * @throws InvalidChildException if the new child is invalid
0741: * @throws PopulateException if there is an error populating the children of this object
0742: */
0743: public void addChild(AbstractChildObject obj, boolean bIsSoftLink)
0744: throws InvalidChildException, PopulateException {
0745: addChild(-1, obj, bIsSoftLink);
0746: }
0747:
0748: /**
0749: * Inserts the specified child object at the specified position
0750: * in the list of children. Shifts the object currently at that
0751: * position (if any) and any subsequent objects to the
0752: * right (adds one to their indices). The <code>boolean</code>
0753: * value <code>bIsSoftLink</code> determines whether this parent
0754: * is the default parent of the child, i.e. a value of <code>true</code>
0755: * specifies a weak relationship and therefore the parent
0756: * is not the default parent of the child.
0757: *
0758: * @param nIndex the index of the new child
0759: * @param obj the new child
0760: * @param bIsSoftLink <code>true</code> if the relationship is weak
0761: * @throws InvalidChildException if the new child is invalid
0762: * @throws PopulateException if there is an error populating the children of this object
0763: */
0764: public void addChild(int nIndex, AbstractChildObject obj,
0765: boolean bIsSoftLink) throws InvalidChildException,
0766: PopulateException {
0767:
0768: try {
0769: if (isLiveVersion() == false) {
0770: throw new InvalidChildException(
0771: "Can't add child to non-live parent");
0772: }
0773: } catch (DataAccessException e) {
0774: throw new PopulateException(e);
0775: }
0776:
0777: if (m_logger.isLoggable(Level.INFO)) {
0778: try {
0779: m_logger.logp(Level.INFO, this .getClass().getName(),
0780: "addChild", "Adding child (id - " + obj.getId()
0781: + ",key - " + obj.getKey()
0782: + ") to parent (id - " + m_nId
0783: + ",key - " + m_nObjectKey + ")");
0784: } catch (DataAccessException e) {
0785: m_logger.log(Level.WARNING, "Trouble logging addChild",
0786: e);
0787: }
0788: }
0789:
0790: //initialise lists
0791: if (m_add_children == null) {
0792: m_add_children = new Vector();
0793: m_add_real_children = new Vector();
0794: }
0795:
0796: //check for validity
0797: if (obj == null) {
0798: if (m_logger.isLoggable(Level.INFO)) {
0799: m_logger.logp(Level.INFO, this .getClass().getName(),
0800: "addChild", "attempt to add null child");
0801: }
0802: throw new InvalidChildException("Null object");
0803: }
0804:
0805: if (isValidChild(obj) == false) {
0806:
0807: throw new InvalidChildException(this .getClass().getName()
0808: + " can't have " + obj.getClass().getName()
0809: + " descendent");
0810:
0811: }
0812:
0813: if (obj.getId() <= 0) {
0814: if (m_logger.isLoggable(Level.INFO)) {
0815: m_logger.logp(Level.INFO, this .getClass().getName(),
0816: "addChild", "Object has invalid id");
0817: }
0818: throw new InvalidChildException("Object has invalid id");
0819: }
0820:
0821: //populate lists if necessary
0822: if (m_bIsChildrenPopulated == false) {
0823: populateChildrenFromDatabase();
0824: }
0825:
0826: try {
0827: //if object isn't already a child register it for addition
0828: if (isChild(obj) == false) {
0829:
0830: if (isNameAllowed(obj) == false) {
0831: throw new InvalidChildException("Duplicate name - "
0832: + obj.getName());
0833: }
0834:
0835: if (obj instanceof AbstractParentObject
0836: && obj.getParents().size() > 0) {
0837: try {
0838: List objpaths = obj.getAllFullPaths();
0839:
0840: Iterator iter = objpaths.iterator();
0841:
0842: while (iter.hasNext()) {
0843: String sPath = (String) iter.next();
0844: if (getPath().startsWith(sPath) == true) {
0845: throw new InvalidChildException(
0846: "Child is not allowed, it's an ancestor of this parent");
0847: }
0848: }
0849:
0850: } catch (DataAccessException e) {
0851: throw new PopulateException(
0852: "Problem occured validating child's path",
0853: e);
0854: }
0855: }
0856:
0857: OrderableCachePointer ptr = new OrderableCachePointer(
0858: CacheHandler.getInstance(m_dsi)
0859: .getCachePointer(obj));
0860:
0861: obj.addEditEventListener(this );
0862:
0863: if (nIndex < 0) {
0864: nIndex = getMaxIndex() + 1;
0865: setMaxIndex(nIndex);
0866: }
0867:
0868: ptr.setPosition(nIndex);
0869:
0870: //ensure a unique position
0871: if (nIndex < getMaxIndex() && getChild(nIndex) != null) {
0872: Set sortedSet = getOrderedChildPointers();
0873: Object[] array = sortedSet.toArray();
0874:
0875: //loop through those object with a higher index and reset
0876: //ensuring that they'll be saved
0877: for (int i = nIndex; i < array.length; i++) {
0878: OrderableCachePointer tmpPtr = (OrderableCachePointer) array[i];
0879: OrderableCachePointer newPtr = new OrderableCachePointer(
0880: tmpPtr);
0881: newPtr.setPosition(tmpPtr.getPosition() + 1);
0882: if (m_remove_children.contains(tmpPtr) == false) {
0883: m_remove_children.add(tmpPtr);
0884: }
0885: if (m_add_children.contains(newPtr) == false) {
0886: m_add_children.add(newPtr);
0887:
0888: //ensure we keep default markers
0889: Integer intId = new Integer(
0890: ((AbstractChildObject) newPtr
0891: .getObject()).getId());
0892: if (m_real_children.contains(intId)) {
0893: m_add_real_children.add(intId);
0894: }
0895: }
0896: }
0897: }
0898:
0899: m_add_children.add(ptr);
0900:
0901: if (bIsSoftLink == false) {
0902: m_add_real_children.add(new Integer(obj.getId()));
0903: }
0904:
0905: m_bIsContentsChanged = true;
0906: }
0907: } catch (DataAccessException e) {
0908: throw new PopulateException(
0909: "Error occured accessing current children", e);
0910: } catch (CacheException e) {
0911: throw new PopulateException(
0912: "Error occured getting cache pointer", e);
0913: }
0914: }
0915:
0916: /**
0917: * Returns <code>true</code> if the child name is allowed.
0918: *
0919: * @param string the name of the new child
0920: * @return <code>true</code> if the child name is allowed
0921: * @throws DataAccessException if there is an error populating this object
0922: * or obtaining the existing children from the appropriate cache
0923: */
0924: private boolean isNameAllowed(AbstractChildObject obj)
0925: throws DataAccessException {
0926: boolean bIsAllowed = true;
0927: String sName = obj.getName();
0928:
0929: if (sName != null) {
0930: try {
0931: if (m_bIsChildrenPopulated == false) {
0932: populateChildrenFromDatabase();
0933: }
0934:
0935: Iterator iter = m_children.iterator();
0936:
0937: while (iter.hasNext() && bIsAllowed == true) {
0938: CachePointer ptr = (CachePointer) iter.next();
0939:
0940: AbstractChildObject child = (AbstractChildObject) ptr
0941: .getObject();
0942:
0943: if (sName.equals(child.getName()) == true) {
0944: if (obj.getLiveVersion() != null
0945: && obj.getLiveVersion().equals(child) == true) {
0946: bIsAllowed = true;
0947: } else {
0948: bIsAllowed = false;
0949: }
0950:
0951: }
0952: }
0953: } catch (CacheException e) {
0954: throw new DataAccessException(e.getLocalizedMessage(),
0955: e);
0956: } catch (PopulateException e) {
0957: throw new DataAccessException(e.getLocalizedMessage(),
0958: e);
0959: }
0960: } else {
0961: bIsAllowed = false;
0962: }
0963:
0964: return bIsAllowed;
0965: }
0966:
0967: /**
0968: * Returns <code>true</code> if the given object is a child of this object.
0969: *
0970: * @param child the potential child object
0971: * @return <code>true</code> if the given object is a child of this object.
0972: *
0973: * @throws DataAccessException if there is an error populating the children of this object
0974: */
0975: public boolean isChild(AbstractChildObject child)
0976: throws DataAccessException {
0977: if (m_bIsChildrenPopulated == false) {
0978: try {
0979: populateChildrenFromDatabase();
0980: } catch (PopulateException e) {
0981: throw new DataAccessException(
0982: "Error occured populating children", e);
0983: }
0984: }
0985:
0986: boolean bRtn = false;
0987:
0988: //construct cache pointer so we can do a simple contains on m_children
0989: CachePointer ptr = null;
0990: try {
0991: ptr = CacheHandler.getInstance(m_dsi)
0992: .getCachePointer(child);
0993: } catch (CacheException e) {
0994: throw new DataAccessException(
0995: "Error occured getting cache pointer:", e);
0996: }
0997:
0998: bRtn = m_children.contains(ptr);
0999:
1000: return bRtn;
1001: }
1002:
1003: /**
1004: * Returns the index of the given child object in the list
1005: * of children.
1006: *
1007: * @param child the child object
1008: * @return the index of the given child object
1009: * @throws DataAccessException if there is an error populating the children of this object
1010: */
1011: public int indexOf(AbstractChildObject child)
1012: throws DataAccessException {
1013: if (m_bIsChildrenPopulated == false) {
1014: try {
1015: populateChildrenFromDatabase();
1016: } catch (PopulateException e) {
1017: throw new DataAccessException(
1018: "Error occured populating kids:"
1019: + e.getLocalizedMessage());
1020: }
1021: }
1022:
1023: //construct cache pointer so we can search through m_children
1024: OrderableCachePointer ptr = null;
1025: try {
1026: ptr = getCachePointer(child);
1027: } catch (CacheException e) {
1028: throw new DataAccessException(
1029: "Error occured getting cache pointer", e);
1030: } catch (PopulateException e) {
1031: throw new DataAccessException(
1032: "Error occured getting cache pointer", e);
1033: }
1034:
1035: int nIndex = -1;
1036:
1037: if (m_children.contains(ptr)) {
1038: nIndex = new ArrayList(getOrderedChildPointers())
1039: .indexOf(ptr);
1040: }
1041:
1042: return nIndex;
1043: }
1044:
1045: /**
1046: * Sets the complete list of children of this object, children will be
1047: * saved with their order position taken from their index in the list.
1048: *
1049: * @param children the list of <code>AbstractChildObject</code>
1050: * @throws InvalidChildException if any of the new children is invalid
1051: * @throws PopulateException if there is an error populating the children of this object
1052: */
1053: public void setChildren(List children)
1054: throws InvalidChildException, PopulateException {
1055:
1056: if (m_bIsChildrenPopulated == false) {
1057: populateChildrenFromDatabase();
1058: }
1059:
1060: if (children.equals(m_children) == false) {
1061: m_add_children = new Vector();
1062: m_remove_children = new Vector();
1063:
1064: try {
1065: List curr_children = getChildren();
1066:
1067: //schedule any current children for deletion if not in the list
1068: Iterator iter = curr_children.iterator();
1069:
1070: while (iter.hasNext()) {
1071: AbstractChildObject tmpChild = (AbstractChildObject) iter
1072: .next();
1073: if (children.contains(tmpChild) == false) {
1074: OrderableCachePointer ptr = getCachePointer(tmpChild);
1075:
1076: m_remove_children.add(ptr);
1077: }
1078: }
1079: } catch (DataAccessException e) {
1080: throw new PopulateException(
1081: "Error occured getting cache pointer", e);
1082: } catch (CacheException e) {
1083: throw new PopulateException(
1084: "Error occured getting cache pointer", e);
1085: }
1086:
1087: for (int i = 0; i < children.size(); i++) {
1088:
1089: AbstractChildObject child;
1090: try {
1091: child = (AbstractChildObject) children.get(i);
1092: } catch (ClassCastException e) {
1093: throw new InvalidChildException();
1094: }
1095:
1096: if (isValidChild(child) == false) {
1097: throw new InvalidChildException();
1098: }
1099:
1100: try {
1101: //if this child is a child already we have to see whether it has
1102: //changed position
1103: if (isChild(child) == true) {
1104: int nIndex = indexOf(child);
1105: OrderableCachePointer ptr = getCachePointer(child);
1106:
1107: if (nIndex != i && ptr.getPosition() != i) {
1108: m_remove_children.add(ptr);
1109: } else {
1110: //break out of loop cause there's nothing to change for this child
1111: continue;
1112: }
1113: }
1114: } catch (DataAccessException e) {
1115: throw new PopulateException(
1116: "Error occured getting cache pointer", e);
1117: } catch (CacheException e) {
1118: throw new PopulateException(
1119: "Error occured getting cache pointer", e);
1120: }
1121:
1122: // construct cache pointer so we can search through m_children
1123: OrderableCachePointer ptr = null;
1124: try {
1125: ptr = new OrderableCachePointer(CacheHandler
1126: .getInstance(m_dsi).getCachePointer(child));
1127: } catch (CacheException e) {
1128: throw new PopulateException(
1129: "Error occured getting cache pointer", e);
1130: }
1131: ptr.setPosition(i);
1132: m_add_children.add(ptr);
1133:
1134: Integer intId = new Integer(child.getId());
1135:
1136: if (m_real_children.contains(intId) == true) {
1137: m_add_real_children.add(intId);
1138: }
1139: }
1140: }
1141:
1142: m_bIsContentsChanged = true;
1143: }
1144:
1145: /**
1146: * Removes the scpecified child from this object's list of children.
1147: *
1148: * @param obj the child to remove
1149: * @throws PopulateException if an error occurs populating the children of this object
1150: */
1151: public void removeChild(AbstractChildObject obj)
1152: throws PopulateException {
1153:
1154: if (m_logger.isLoggable(Level.INFO)) {
1155: try {
1156: m_logger.logp(Level.INFO, this .getClass().getName(),
1157: "removeChild", "Removing child "
1158: + obj.getClass().getName() + ", key - "
1159: + obj.getKey() + " from parent, key - "
1160: + this .getKey());
1161: } catch (DataAccessException e) {
1162: m_logger.log(Level.WARNING,
1163: "Problem logging child removal", e);
1164: }
1165: }
1166:
1167: if (m_remove_children == null) {
1168: m_remove_children = new Vector();
1169: }
1170:
1171: String sClassName = obj.getClass().getName();
1172:
1173: //if it's a valid child register it for removal
1174: if (isValidChild(obj) == true) {
1175: if (m_bIsChildrenPopulated == false) {
1176: populateChildrenFromDatabase();
1177: }
1178:
1179: // construct cache pointer so we can do a simple remove on m_children
1180: CachePointer ptr = null;
1181: try {
1182: ptr = CacheHandler.getInstance(m_dsi).getCachePointer(
1183: obj);
1184: } catch (CacheException e) {
1185: throw new PopulateException(
1186: "Error occured getting cache pointer", e);
1187: }
1188:
1189: int nPtrIndex = m_children.indexOf(ptr);
1190:
1191: if (nPtrIndex >= 0) {
1192: OrderableCachePointer ordPtr = (OrderableCachePointer) m_children
1193: .get(nPtrIndex);
1194:
1195: if (m_remove_children.contains(ordPtr) == false) {
1196: m_remove_children.add(ordPtr);
1197: }
1198:
1199: Set sortedSet = getOrderedChildPointers();
1200:
1201: Object[] array = sortedSet.toArray();
1202:
1203: //loop through those object with a higher index and reset
1204: //ensuring that they'll be saved
1205:
1206: int nIndex = ordPtr.getPosition();
1207: if (nIndex >= 0) {
1208: for (int i = nIndex; i < array.length; i++) {
1209: OrderableCachePointer tmpPtr = (OrderableCachePointer) array[i];
1210: if (tmpPtr.equals(ordPtr) == false) {
1211: OrderableCachePointer newPtr = new OrderableCachePointer(
1212: tmpPtr);
1213: newPtr
1214: .setPosition(tmpPtr.getPosition() - 1);
1215:
1216: if (m_remove_children.contains(tmpPtr) == false) {
1217: m_remove_children.add(tmpPtr);
1218: }
1219:
1220: if (m_add_children.contains(newPtr) == false) {
1221: m_add_children.add(newPtr);
1222: // ensure we keep default markers
1223: try {
1224: Integer intId = new Integer(
1225: ((AbstractChildObject) newPtr
1226: .getObject())
1227: .getId());
1228: if (m_real_children.contains(intId)) {
1229: m_add_real_children.add(intId);
1230: }
1231: } catch (CacheException e) {
1232: throw new PopulateException(e
1233: .getLocalizedMessage(), e);
1234: }
1235: }
1236: }
1237: }
1238: resetMaxIndex();
1239: }
1240: m_bIsContentsChanged = true;
1241: }
1242: }
1243: }
1244:
1245: /**
1246: * @throws PopulateException
1247: *
1248: */
1249: private void resetMaxIndex() throws PopulateException {
1250: populateChildrenFromDatabase();
1251:
1252: Iterator iter = m_children.iterator();
1253:
1254: int nMaxIndex = m_nMaxIndex;
1255: while (iter.hasNext()) {
1256: OrderableCachePointer ptr = (OrderableCachePointer) iter
1257: .next();
1258: int nIndex = ptr.getPosition();
1259: if (nIndex > nMaxIndex) {
1260: nMaxIndex = nIndex;
1261: }
1262: }
1263: m_nMaxIndex = nMaxIndex;
1264: }
1265:
1266: /**
1267: * Returns <code>true</code> if the given object is of a valid
1268: * type to become a child of this object.
1269: *
1270: * @param childObj the child object to test
1271: * @return <code>true</code> if the given object is of a valid
1272: * type to become a child of this object.
1273: */
1274: public boolean isValidChild(AbstractChildObject childObj) {
1275:
1276: if (m_logger.isLoggable(Level.FINER)) {
1277: try {
1278: m_logger.logp(Level.FINER, this .getClass().getName(),
1279: "isValidChild", "Validating child - "
1280: + childObj.getClass().getName()
1281: + " key - " + childObj.getKey());
1282: } catch (DataAccessException e) {
1283: m_logger.log(Level.WARNING,
1284: "Error logging child validation", e);
1285: }
1286: }
1287:
1288: List classes = new ArrayList();
1289: List classNames = getChildClassNames();
1290:
1291: Iterator iter = classNames.iterator();
1292: boolean bIsValid = false;
1293: while (iter.hasNext() && bIsValid == false) {
1294: String sClassname = (String) iter.next();
1295: Class clss;
1296: try {
1297: clss = Class.forName(sClassname);
1298: if (clss.isAssignableFrom(childObj.getClass())) {
1299: bIsValid = true;
1300: }
1301: } catch (ClassNotFoundException e) {
1302: bIsValid = false;
1303: }
1304:
1305: }
1306:
1307: return bIsValid;
1308: }
1309:
1310: /**
1311: * Returns all children that match the class specified.
1312: *
1313: * @param clss the class to match children against
1314: * @return a list of children that match the specified class
1315: * @throws DataAccessException if there is an error accessing
1316: * the children of this parent
1317: */
1318: public List getChildrenByClass(Class clss)
1319: throws DataAccessException {
1320: List rtn = new Vector();
1321:
1322: Iterator iter = getChildren().iterator();
1323:
1324: while (iter.hasNext()) {
1325: Object obj = iter.next();
1326: if (clss.isInstance(obj) == true) {
1327: rtn.add(obj);
1328: }
1329: }
1330:
1331: return rtn;
1332: }
1333:
1334: /**
1335: * Returns a list of all children of the given type,
1336: * i.e. branch or leaf children.
1337: *
1338: * @param nType the type code which determines which children
1339: * are returned
1340: * @return a list of all children of the given type
1341: * @throws DataAccessException if there is an error accessing
1342: * the children of this parent
1343: */
1344: public List getChildrenByType(int nType) throws DataAccessException {
1345: List rtn = new Vector();
1346:
1347: //if this is not a live version then get the kids from the live version
1348: if (isLiveVersion() == false) {
1349: AbstractParentObject parent = (AbstractParentObject) getLiveVersion();
1350:
1351: if (parent != null) {
1352: rtn = parent.getChildrenByType(nType);
1353: }
1354: } else {
1355: //populate kids if necessary
1356: if (m_bIsChildrenPopulated == false) {
1357: try {
1358: populateChildrenFromDatabase();
1359: } catch (PopulateException e) {
1360: throw new DataAccessException(
1361: "Error occured populating kids:", e);
1362: }
1363: }
1364:
1365: rtn = new Vector();
1366:
1367: Iterator iter = m_children.iterator();
1368:
1369: //loop through getting object from cache pointers and adding to
1370: //list as appropriate
1371: while (iter.hasNext()) {
1372: CachePointer ptr = (CachePointer) iter.next();
1373:
1374: AbstractChildObject child = null;
1375:
1376: try {
1377: child = (AbstractChildObject) ptr.getObject();
1378: } catch (CacheException e) {
1379: throw new DataAccessException(
1380: "Error occured getting object from cache:"
1381: + e.getLocalizedMessage());
1382: }
1383:
1384: if (nType == ALL_NODES) {
1385: rtn.add(child);
1386: } else {
1387:
1388: if (nType == BRANCH_NODES
1389: && child instanceof AbstractParentObject) {
1390: rtn.add(child);
1391: } else if (nType == LEAF_NODES
1392: && (child instanceof AbstractParentObject) == false) {
1393: rtn.add(child);
1394: }
1395: }
1396: }
1397: }
1398:
1399: return rtn;
1400: }
1401:
1402: /**
1403: * Returns a list of all children of this object.
1404: *
1405: * @return a list of all children of this object
1406: * @throws DataAccessException if there is an error accessing the children of this object
1407: */
1408: public List getChildren() throws DataAccessException {
1409:
1410: if (m_logger.isLoggable(Level.FINE)) {
1411: m_logger.logp(Level.FINE, this .getClass().getName(),
1412: "getChildren", "Getting children for object, id - "
1413: + m_nId + ", key - " + m_nObjectKey);
1414: }
1415:
1416: List rtn = getChildrenByType(ALL_NODES);
1417:
1418: return rtn;
1419: }
1420:
1421: /**
1422: * Returns the child with the specified name, otherwise returns a null.
1423: *
1424: * @param string the name of the desired child object
1425: * @returnthe child with the specified name, otherwise returns a <code>null</code>
1426: */
1427: public AbstractChildObject getChildByName(String sName)
1428: throws DataAccessException {
1429: AbstractChildObject child = null;
1430:
1431: if (isLiveVersion() == false) {
1432: AbstractParentObject parent = (AbstractParentObject) getLiveVersion();
1433:
1434: if (parent != null) {
1435: child = parent.getChildByName(sName);
1436: }
1437: } else {
1438: //populate kids if necessary
1439: if (m_bIsChildrenPopulated == false) {
1440: try {
1441: populateChildrenFromDatabase();
1442: } catch (PopulateException e) {
1443: throw new DataAccessException(
1444: "Error occured populating kids:", e);
1445: }
1446: }
1447:
1448: Iterator iter = m_children.iterator();
1449:
1450: try {
1451: while (iter.hasNext() == true && child == null) {
1452: CachePointer tmpPtr = (CachePointer) iter.next();
1453: AbstractChildObject tmpChild = (AbstractChildObject) tmpPtr
1454: .getObject();
1455:
1456: if (tmpChild.getName().equals(sName)) {
1457: child = tmpChild;
1458: }
1459: }
1460: } catch (CacheException e) {
1461: throw new DataAccessException(e.getLocalizedMessage(),
1462: e);
1463: }
1464:
1465: }
1466:
1467: return child;
1468: }
1469:
1470: /**
1471: * Returns the child of this parent object at the specified
1472: * index.
1473: *
1474: * @param nIndex the index of the desired child object
1475: * @return the index of the desired child object
1476: * @throws DataAccessException if there is an error populating the children of this object
1477: */
1478: public AbstractChildObject getChild(int nIndex)
1479: throws DataAccessException {
1480: AbstractChildObject child = null;
1481:
1482: if (nIndex >= 0) {
1483: try {
1484: if (m_bIsChildrenPopulated == false) {
1485: populateChildrenFromDatabase();
1486: }
1487:
1488: Iterator iter = m_children.iterator();
1489: boolean bFound = false;
1490: while (iter.hasNext() && bFound == false) {
1491: OrderableCachePointer ptr = (OrderableCachePointer) iter
1492: .next();
1493: if (ptr.getPosition() == nIndex) {
1494: child = (AbstractChildObject) ptr.getObject();
1495: bFound = true;
1496: }
1497: }
1498: } catch (PopulateException e) {
1499: throw new DataAccessException(
1500: "Error populating parent", e);
1501: } catch (CacheException e) {
1502: throw new DataAccessException(
1503: "Error getting object from cache", e);
1504: }
1505: }
1506:
1507: return child;
1508: }
1509:
1510: /**
1511: * Returns the current maximum child index position.
1512: *
1513: * Note: this isn't the same as size as there is no enforcement rules on
1514: * index values being contiguous.
1515: *
1516: * @return the current maximum child index position
1517: * @throws DataAccessException if an error occurs populating the children of this object
1518: */
1519: public int getMaxIndex() throws DataAccessException {
1520: if (m_nMaxIndex < 0 && m_bIsChildrenPopulated == false) {
1521: try {
1522: populateChildrenFromDatabase();
1523: } catch (PopulateException e) {
1524: throw new DataAccessException(
1525: "Error populating children", e);
1526: }
1527: }
1528:
1529: return m_nMaxIndex;
1530: }
1531:
1532: /**
1533: * Sets the value of the field containing the maximum child index position.
1534: *
1535: * @param nMax the maximum child index position
1536: */
1537: private void setMaxIndex(int nMax) {
1538: m_nMaxIndex = nMax;
1539: }
1540:
1541: /**
1542: * Returns the archived child with the specified name,
1543: * otherwise returns a <code>null</code>.
1544: *
1545: * @param sName the name of the archived child to return
1546: * @return the archived child with the specified name
1547: * @throws DataAccessException if there is an error populating the list of archived children
1548: */
1549: public AbstractChildObject getArchivedChildByName(String sName)
1550: throws DataAccessException {
1551: AbstractChildObject child = null;
1552: List children = getArchivedChildren();
1553:
1554: Iterator iter = children.iterator();
1555:
1556: while (iter.hasNext() == true && child == null) {
1557: AbstractChildObject tmpChild = (AbstractChildObject) iter
1558: .next();
1559:
1560: if (tmpChild.getName().equals(sName)) {
1561: child = tmpChild;
1562: }
1563: }
1564:
1565: return child;
1566: }
1567:
1568: /**
1569: * Returns a list of archived children which match the specified
1570: * <code>Class</code>.
1571: *
1572: * @param clss the <code>Class</code> to match children by
1573: * @return the list of archived children
1574: * @throws DataAccessException if there is an error populating the
1575: * list of archived children
1576: */
1577: public List getArchivedChildrenByClass(Class clss)
1578: throws DataAccessException {
1579: List rtn = new Vector();
1580:
1581: Iterator iter = getArchivedChildren().iterator();
1582:
1583: while (iter.hasNext()) {
1584: Object obj = iter.next();
1585:
1586: if (clss.isInstance(obj) == true) {
1587: rtn.add(obj);
1588: }
1589: }
1590:
1591: return rtn;
1592: }
1593:
1594: /**
1595: * Returns a list of archived children matching the type specified,
1596: * i.e. leaf, branch or all children.
1597: *
1598: * @param nType the type code of children to return
1599: * @return a list of archived children matching the type specified
1600: * @throws DataAccessException if there is an error populating the
1601: * list of archived children
1602: */
1603: public List getArchivedChildrenByType(int nType)
1604: throws DataAccessException {
1605: List rtn = null;
1606:
1607: if (nType == ALL_NODES) {
1608: rtn = getArchivedChildren();
1609: } else {
1610: rtn = new Vector();
1611:
1612: List archive = getArchivedChildren();
1613:
1614: Iterator iter = archive.iterator();
1615:
1616: //loop through adding kids as appropriate
1617: while (iter.hasNext()) {
1618: AbstractChildObject child = (AbstractChildObject) iter
1619: .next();
1620:
1621: if (nType == BRANCH_NODES
1622: && child instanceof AbstractParentObject) {
1623: rtn.add(child);
1624: } else if (nType == LEAF_NODES
1625: && (child instanceof AbstractParentObject) == false) {
1626: rtn.add(child);
1627: }
1628: }
1629: }
1630:
1631: return rtn;
1632: }
1633:
1634: /**
1635: * Returns a list of archived descendants matching the specified
1636: * <code>Class</code>.
1637: *
1638: * @param clss the <code>Class</code> to match by
1639: * @return the list of archived descendants matching the specified
1640: * <code>Class</code>
1641: * @throws DataAccessException if there is an error populating the
1642: * list of archived children
1643: */
1644: public List getAllArchivedChildrenByClass(Class clss)
1645: throws DataAccessException {
1646: List rtn = new Vector();
1647:
1648: Iterator iter = getAllArchivedChildren().iterator();
1649:
1650: while (iter.hasNext()) {
1651: Object obj = iter.next();
1652:
1653: if (clss.isInstance(obj) == true) {
1654: rtn.add(obj);
1655: }
1656: }
1657:
1658: return rtn;
1659: }
1660:
1661: /**
1662: * Returns a list of all archived descendants, i.e. of this object and
1663: * its branch children, of the specified type, i.e. leaf, branch or all
1664: * children
1665: *
1666: * @param nType the type code of children to return
1667: * @return a list of all archived descendants of the specified type
1668: * @throws DataAccessException if there is an error populating the
1669: * list of archived children
1670: */
1671: public List getAllArchivedChildrenByType(int nType)
1672: throws DataAccessException {
1673: List rtn = null;
1674:
1675: if (nType == ALL_NODES) {
1676: rtn = this .getAllArchivedChildren();
1677: } else {
1678: rtn = new Vector();
1679:
1680: List archive = this .getAllArchivedChildren();
1681:
1682: Iterator iter = archive.iterator();
1683:
1684: //loop though adding kids as appropriate
1685: while (iter.hasNext()) {
1686: AbstractChildObject child = (AbstractChildObject) iter
1687: .next();
1688:
1689: if (nType == BRANCH_NODES
1690: && child instanceof AbstractParentObject) {
1691: rtn.add(child);
1692: } else if (nType == LEAF_NODES
1693: && (child instanceof AbstractParentObject) == false) {
1694: rtn.add(child);
1695: }
1696: }
1697: }
1698:
1699: return rtn;
1700: }
1701:
1702: /**
1703: * Returns <code>true</code> if this parent is a top level parent
1704: * in the hierarchy of relationships,
1705: * i.e. if it is not a child of another parent
1706: *
1707: * @return <code>true</code> if this parent is a top level parent
1708: */
1709: public boolean isTopLevel() {
1710: boolean bIsTopLevel = false;
1711:
1712: try {
1713: bIsTopLevel = (this .getParents().size() == 0);
1714: } catch (Exception e) {
1715: m_logger.log(Level.WARNING, e.getMessage(), e);
1716: }
1717:
1718: return bIsTopLevel;
1719: }
1720:
1721: /* (non-Javadoc)
1722: * @see org.openharmonise.rm.resources.lifecycle.Editable#changeStatus(org.openharmonise.rm.resources.lifecycle.Status)
1723: */
1724: public Editable changeStatus(Status status) throws EditException {
1725: boolean bIsUnApproved = false;
1726:
1727: try {
1728: if (getStatus() == Status.UNAPPROVED) {
1729: bIsUnApproved = true;
1730: }
1731: } catch (DataAccessException e) {
1732: throw new EditException("Error occured getting status:"
1733: + e.getLocalizedMessage());
1734: }
1735:
1736: if ((bIsUnApproved == true) && (status == Status.APPROVED)) {
1737: // if there is a live version copy the child group links
1738: AbstractParentObject live = null;
1739: try {
1740: live = (AbstractParentObject) getLiveVersion();
1741: } catch (DataAccessException e) {
1742: throw new EditException(
1743: "Error occured finding live version", e);
1744: }
1745:
1746: if (live != null) {
1747: try {
1748: List childClassNames = getChildClassNames();
1749: Iterator iter = childClassNames.iterator();
1750: UpdateStatement update = new UpdateStatement();
1751: int nParentKey = live.getKey();
1752:
1753: while (iter.hasNext()) {
1754: String sClassname = (String) iter.next();
1755: String sTable = DatabaseInfo.getInstance()
1756: .getTableName(sClassname);
1757:
1758: ColumnRef colGroup = getGroupChildJoinColumnRef(
1759: sTable, TAG_GROUP);
1760:
1761: update.addColumnValue(colGroup,
1762: this .m_nObjectKey);
1763: update.addWhereCondition(colGroup, "=",
1764: nParentKey);
1765:
1766: m_dsi.execute(update);
1767:
1768: update.clear();
1769: }
1770:
1771: live.clearChildren();
1772:
1773: } catch (DataStoreException dse) {
1774: throw new EditException(
1775: "Error occured updating DB", dse);
1776: } catch (DataAccessException dae) {
1777: throw new EditException(
1778: "Error accessing data for update", dae);
1779: }
1780: }
1781: }
1782:
1783: return super .changeStatus(status);
1784: }
1785:
1786: /**
1787: * Returns a list of top level parent objects of a specified object type.
1788: *
1789: * @param dbinterf the data store interface
1790: * @param grpObj an instance of the type of object to be returned
1791: * @return a list of top level parent objects
1792: * @throws DataAccessException if there is any errors building the
1793: * list of objects
1794: */
1795: public static List getTopLevelGroups(
1796: AbstractDataStoreInterface dbinterf,
1797: AbstractParentObject grpObj) throws DataAccessException {
1798: Vector vTopLevelGroups = new Vector(16);
1799: ResultSet rs = null;
1800:
1801: try {
1802: String sTable = null;
1803: try {
1804: sTable = DatabaseInfo.getInstance().getTableName(
1805: grpObj.getClass().getName());
1806: } catch (DataStoreException e) {
1807: throw new DataAccessException(
1808: "Error getting table name", e);
1809: }
1810:
1811: SelectStatement nestedSelect = new SelectStatement();
1812:
1813: nestedSelect.addSelectColumn(grpObj
1814: .getGroupChildJoinColumnRef(sTable, TAG_CHILDREN));
1815:
1816: SelectStatement select = new SelectStatement();
1817:
1818: select.addSelectColumn(grpObj.getInstanceColumnRef(
1819: ATTRIB_ID, false));
1820:
1821: select.addWhereCondition(grpObj.getInstanceColumnRef(
1822: ATTRIB_KEY, false), "NOT IN", nestedSelect);
1823:
1824: select.addWhereCondition(grpObj.getInstanceColumnRef(
1825: ATTRIB_TYPE, false), "=", grpObj.getClass()
1826: .getName());
1827:
1828: rs = dbinterf.execute(select);
1829:
1830: while (rs.next()) {
1831: Publishable topObj = HarmoniseObjectFactory
1832: .instantiatePublishableObject(dbinterf, grpObj
1833: .getClass().getName(), rs.getInt(1));
1834: vTopLevelGroups.addElement(topObj);
1835: }
1836: } catch (DataStoreException e) {
1837: throw new DataAccessException(
1838: "Error occured building query", e);
1839: } catch (HarmoniseFactoryException e) {
1840: throw new DataAccessException(
1841: "Error occured instantiating object from factory",
1842: e);
1843: } catch (SQLException e) {
1844: throw new DataAccessException("SQL error", e);
1845: } finally {
1846: if (rs != null) {
1847: try {
1848: rs.close();
1849: } catch (SQLException e) {
1850: throw new DataAccessException(
1851: "Error occured closing result set", e);
1852: }
1853: }
1854: }
1855:
1856: return vTopLevelGroups;
1857: }
1858:
1859: /**
1860: * Returns a list of class names of classes that can be children of this
1861: * object.
1862: */
1863: abstract public List getChildClassNames();
1864:
1865: /**
1866: * Returns a list all archived children of this parent object
1867: *
1868: * @return the list of archived children
1869: * @throws DataAccessException if there is an error populating the
1870: * list of archived children
1871: */
1872: public List getArchivedChildren() throws DataAccessException {
1873: try {
1874:
1875: if (isLiveVersion() == false) {
1876: AbstractParentObject liveVersion = (AbstractParentObject) getLiveVersion();
1877:
1878: if (liveVersion != null) {
1879: m_archivedChildren = liveVersion
1880: .getArchivedChildren();
1881: } else if (isHistorical() == true) {
1882: if (m_bIsArchivedChildrenPopulated == false) {
1883: populateArchivedChildrenFromDatabase(false);
1884: }
1885: } else {
1886: m_archivedChildren = new Vector();
1887: }
1888: } else {
1889: if (m_bIsArchivedChildrenPopulated == false) {
1890: populateArchivedChildrenFromDatabase(false);
1891: }
1892: }
1893: } catch (PopulateException e) {
1894: throw new DataAccessException(
1895: "Error occured populating archived children", e);
1896: }
1897:
1898: //return a differnet List to protect the memeber variable from modification
1899: return new ArrayList(m_archivedChildren);
1900: }
1901:
1902: /*----------------------------------------------------------------------------
1903: Protected Methods
1904: -----------------------------------------------------------------------------*/
1905:
1906: /**
1907: * Returns the column reference for a column in the parent-child
1908: * relationship table.
1909: *
1910: * @param sChildTableName the relationship table name
1911: * @param sCol the column, tag or attribute name
1912: * @return the corresponding column reference
1913: * @throws DataStoreException if the <code>sCol</code> reference is invalid
1914: */
1915: protected ColumnRef getGroupChildJoinColumnRef(
1916: String sChildTableName, String sCol)
1917: throws DataStoreException {
1918: ColumnRef colref = null;
1919:
1920: StringBuffer sbuf = new StringBuffer(getDBTableName()).append(
1921: JOIN_TO).append(sChildTableName);
1922:
1923: String sTable = sbuf.toString();
1924:
1925: if (sCol.equals(CLMN_CHILD_KEY) == true
1926: || sCol.equals(AbstractParentObject.TAG_CHILDREN)) {
1927: colref = new ColumnRef(sTable, CLMN_CHILD_KEY,
1928: ColumnRef.NUMBER);
1929: } else if (sCol.equals(CLMN_PARENT_KEY) == true
1930: || sCol.equals(TAG_GROUP) == true) {
1931: colref = new ColumnRef(sTable, CLMN_PARENT_KEY,
1932: ColumnRef.NUMBER);
1933: } else if (sCol.equals(CLMN_DATE) == true) {
1934: colref = new ColumnRef(sTable, CLMN_DATE, ColumnRef.DATE);
1935: } else if (sCol.equals(CLMN_DEFAULT_LINK) == true
1936: || sCol.equals(ATTRIB_DEFAULT) == true) {
1937: colref = new ColumnRef(sTable, CLMN_DEFAULT_LINK,
1938: ColumnRef.NUMBER);
1939: } else if (sCol.equals(CLMN_POSITION) == true) {
1940: colref = new ColumnRef(sTable, CLMN_POSITION,
1941: ColumnRef.NUMBER);
1942: }
1943:
1944: if (colref == null) {
1945: throw new InvalidColumnReferenceException();
1946: }
1947:
1948: return colref;
1949: }
1950:
1951: /* (non-Javadoc)
1952: * @see org.openharmonise.rm.resources.AbstractEditableObject#delete(boolean)
1953: */
1954: protected void delete(boolean bDeleteHist)
1955: throws DataStoreException, DataAccessException,
1956: EditException, PopulateException {
1957:
1958: try {
1959: List children = getChildren();
1960:
1961: if (children.isEmpty() == false) {
1962: while (children.size() > 0) {
1963: AbstractChildObject child = (AbstractChildObject) children
1964: .get(0);
1965:
1966: if (child.getRealParent().equals(this )) {
1967: child.delete(bDeleteHist);
1968: } else {
1969: removeChild(child);
1970: saveNonCoreData();
1971: }
1972: }
1973:
1974: m_children.clear();
1975: }
1976: } catch (DataAccessException e) {
1977: throw new EditException("Error occured accesing data", e);
1978: } catch (PopulateException e) {
1979: throw new EditException("Error occured populating object",
1980: e);
1981: } catch (DataStoreException e) {
1982: throw new EditException(
1983: "Error occured deleting branch child", e);
1984: }
1985:
1986: super .delete(bDeleteHist);
1987: }
1988:
1989: /* (non-Javadoc)
1990: * @see org.openharmonise.rm.resources.AbstractEditableObject#saveNonCoreData()
1991: */
1992: protected void saveNonCoreData() throws EditException {
1993: boolean bIsApproved = false;
1994:
1995: if (isLockThread() == false) {
1996: throw new EditException(
1997: "This object has been locked by another thread");
1998: }
1999:
2000: try {
2001: if (getStatus() == Status.APPROVED) {
2002: bIsApproved = true;
2003: }
2004: } catch (DataAccessException e) {
2005: throw new EditException("Error occured getting status", e);
2006: }
2007:
2008: if (bIsApproved == true && isHistorical() == false) {
2009: //ensure children variables populated for resave
2010: try {
2011: populateChildrenFromDatabase();
2012: } catch (PopulateException e) {
2013: throw new EditException(
2014: "Error occured populating object", e);
2015: }
2016:
2017: //save changes in content (ie group children, link data) without creating unapproved version.
2018: if (m_bIsContentsChanged == true) {
2019: //remove children to be removed
2020: DeleteStatement delete = new DeleteStatement();
2021:
2022: Iterator remove_iter = new Vector(m_remove_children)
2023: .listIterator();
2024:
2025: while (remove_iter.hasNext()) {
2026: try {
2027: OrderableCachePointer ptr = (OrderableCachePointer) remove_iter
2028: .next();
2029: AbstractChildObject child = (AbstractChildObject) ptr
2030: .getObject();
2031:
2032: if (m_logger.isLoggable(Level.FINER)) {
2033: m_logger.logp(Level.FINER, this .getClass()
2034: .getName(), "saveNonCoreData",
2035: "Deleteing DB child entry for "
2036: + child.getClass()
2037: .getName()
2038: + ", id - " + child.getId()
2039: + "posn -"
2040: + ptr.getPosition()
2041: + "for parent ,id - "
2042: + getId());
2043: }
2044:
2045: // if the m_add_children vector also contains
2046: // the object to be removed, then
2047: // remove it from the m_add_children vector
2048: if (m_add_children.contains(ptr) == true) {
2049: m_add_children.remove(ptr);
2050: }
2051:
2052: child.removeParent(this );
2053: child.removeEditEventListener(this );
2054:
2055: Integer intId = new Integer(child.getId());
2056: boolean bIsReal = m_real_children
2057: .contains(intId);
2058:
2059: delete
2060: .addWhereCondition(
2061: getGroupChildJoinColumnRef(
2062: child.getDBTableName(),
2063: TAG_CHILDREN), "=",
2064: child.getKey());
2065: delete.addWhereCondition(
2066: getGroupChildJoinColumnRef(child
2067: .getDBTableName(), TAG_GROUP),
2068: "=", m_nObjectKey);
2069: delete.addWhereCondition(
2070: getGroupChildJoinColumnRef(child
2071: .getDBTableName(),
2072: CLMN_POSITION), "=", ptr
2073: .getPosition());
2074:
2075: m_dsi.execute(delete);
2076:
2077: delete.clear();
2078:
2079: m_children.remove(ptr);
2080:
2081: if (bIsReal == true) {
2082: m_real_children.remove(intId);
2083: }
2084: } catch (DataAccessException e) {
2085: throw new EditException(
2086: "Error accessing child data", e);
2087: } catch (CacheException e) {
2088: throw new EditException(
2089: "Error getting object from cache", e);
2090: } catch (DataStoreException e) {
2091: throw new EditException(
2092: "Error deleting data from DB", e);
2093: } catch (PopulateException e) {
2094: throw new EditException(
2095: "Error removing groups as parent from child",
2096: e);
2097: }
2098: }
2099:
2100: //clear list
2101: m_remove_children.clear();
2102:
2103: InsertStatement insert = new InsertStatement();
2104:
2105: //insert new children
2106: Iterator iter = this .m_add_children.listIterator();
2107:
2108: int nDefault = 0;
2109:
2110: while (iter.hasNext()) {
2111: try {
2112: OrderableCachePointer ptr = (OrderableCachePointer) iter
2113: .next();
2114: AbstractChildObject child = (AbstractChildObject) ptr
2115: .getObject();
2116:
2117: if (m_logger.isLoggable(Level.FINER)) {
2118: m_logger.logp(Level.FINER, this .getClass()
2119: .getName(), "saveNonCoreData",
2120: "Adding DB child entry for "
2121: + child.getClass()
2122: .getName()
2123: + ", id - " + child.getId()
2124: + " for parent ,id - "
2125: + getId());
2126: }
2127:
2128: Integer intId = new Integer(child.getId());
2129: boolean bIsReal = m_add_real_children
2130: .contains(intId);
2131:
2132: if (bIsReal == true) {
2133: child.setRealParent(this );
2134: } else {
2135: child.addParent(this );
2136: }
2137: child.addEditEventListener(this );
2138:
2139: insert
2140: .addColumnValue(
2141: getGroupChildJoinColumnRef(
2142: child.getDBTableName(),
2143: TAG_CHILDREN), child
2144: .getKey());
2145: insert.addColumnValue(
2146: getGroupChildJoinColumnRef(child
2147: .getDBTableName(), TAG_GROUP),
2148: m_nObjectKey);
2149:
2150: nDefault = 0;
2151:
2152: if (bIsReal == true) {
2153: nDefault = 1;
2154: }
2155:
2156: insert
2157: .addColumnValue(
2158: getGroupChildJoinColumnRef(
2159: child.getDBTableName(),
2160: AbstractChildObject.CLMN_DEFAULT_LINK),
2161: nDefault);
2162:
2163: insert.addColumnValue(
2164: getGroupChildJoinColumnRef(child
2165: .getDBTableName(),
2166: CLMN_POSITION), ptr
2167: .getPosition());
2168:
2169: m_dsi.execute(insert);
2170:
2171: insert.clear();
2172: m_children.add(ptr);
2173: if (bIsReal == true) {
2174: m_real_children.add(intId);
2175: }
2176:
2177: iter.remove();
2178:
2179: } catch (DataAccessException e) {
2180: throw new EditException(
2181: "Error occured accessing data", e);
2182: } catch (DataStoreException e) {
2183: throw new EditException(
2184: "Error occured inserting child data to DB",
2185: e);
2186: } catch (CacheException e) {
2187: throw new EditException(
2188: "Error occured getting object from cache",
2189: e);
2190: } catch (PopulateException e) {
2191: throw new EditException(
2192: "Error occured adding group to child",
2193: e);
2194: }
2195:
2196: }
2197:
2198: //clear 'add' lists
2199: m_add_children.clear();
2200: m_add_real_children.clear();
2201:
2202: //get sorted kids so we don't order them all the time
2203: m_children = new ArrayList(getOrderedChildPointers());
2204:
2205: m_bIsContentsChanged = false;
2206: }
2207: }
2208: }
2209:
2210: /* (non-Javadoc)
2211: * @see org.openharmonise.rm.resources.AbstractObject#fullPopulate()
2212: */
2213: protected void fullPopulate() throws PopulateException {
2214: if (m_bIsChildrenPopulated == false) {
2215: populateChildrenFromDatabase();
2216: }
2217:
2218: super .fullPopulate();
2219: }
2220:
2221: /**
2222: * Adds the object which the given XML element represents as a
2223: * child to this parent object.
2224: *
2225: * @param el the XML element representing the new child
2226: * @throws InvalidChildException if the new child is invalid for this
2227: * parent
2228: * @throws PopulateException if there is an error creating an object
2229: * from the XML element
2230: */
2231: protected void addChild(Element el) throws InvalidChildException,
2232: PopulateException {
2233:
2234: AbstractChildObject obj;
2235: try {
2236: obj = (AbstractChildObject) HarmoniseObjectFactory
2237: .instantiatePublishableObject(this .m_dsi, el, null);
2238:
2239: this .addChild(obj);
2240: } catch (HarmoniseFactoryException e) {
2241: throw new PopulateException(
2242: "Problem occured creating object from XML element",
2243: e);
2244: }
2245:
2246: }
2247:
2248: /**
2249: * Removes the object which is represented by the given XML element
2250: * from the children of this object.
2251: *
2252: * @param el the XML element representing a child
2253: * @throws PopulateException if an error occurs creating an object from the XML element
2254: */
2255: protected void removeChild(Element el) throws PopulateException {
2256:
2257: try {
2258: AbstractChildObject obj = (AbstractChildObject) HarmoniseObjectFactory
2259: .instantiatePublishableObject(this .m_dsi, el, null);
2260:
2261: removeChild(obj);
2262: } catch (HarmoniseFactoryException e) {
2263: throw new PopulateException(
2264: "Error occured creating object from XML", e);
2265: }
2266:
2267: }
2268:
2269: /*----------------------------------------------------------------------------
2270: Private Functions
2271: -----------------------------------------------------------------------------*/
2272:
2273: /**
2274: * Clears the list of archived children.
2275: *
2276: */
2277: private void clearArchivedChildren() {
2278: m_archivedChildren = null;
2279: m_allArchivedChildren = null;
2280: m_bIsArchivedChildrenPopulated = false;
2281: m_bIsAllArchivedChildrenPopulated = false;
2282: }
2283:
2284: /**
2285: * Returns all archived descendants of this object.
2286: *
2287: * @return the list of archived descendants
2288: * @throws DataAccessException if an error occurs while populating the
2289: * list of archived descendants
2290: */
2291: private List getAllArchivedChildren() throws DataAccessException {
2292: try {
2293: if (isPopulated() == false) {
2294: populateFromDatabase();
2295: }
2296:
2297: if (isLiveVersion() == false) {
2298: AbstractParentObject liveVersion = (AbstractParentObject) getLiveVersion();
2299:
2300: if (liveVersion != null) {
2301: m_allArchivedChildren = liveVersion
2302: .getAllArchivedChildren();
2303: } else {
2304: m_allArchivedChildren = new Vector();
2305: }
2306: } else {
2307: if (m_bIsAllArchivedChildrenPopulated == false) {
2308: populateArchivedChildrenFromDatabase(true);
2309: }
2310: }
2311: } catch (PopulateException e) {
2312: throw new DataAccessException(
2313: "Error occured populating archived children", e);
2314: }
2315:
2316: return new ArrayList(m_allArchivedChildren);
2317: }
2318:
2319: /**
2320: * Returns an object from the factory appropriate to the
2321: * given element.
2322: *
2323: * @param el the XML representing an object
2324: * @return the object created from the XML element
2325: * @throws HarmoniseFactoryException if an error occurs accessing the object
2326: * from the <code>HarmoniseObjectFactory</code>
2327: */
2328: private Object getObjectFromFactory(Element el)
2329: throws HarmoniseFactoryException {
2330: return (Object) HarmoniseObjectFactory
2331: .instantiatePublishableObject(this .m_dsi, el, null);
2332: }
2333:
2334: /**
2335: * Returns <code>true</code> if specified child is a child of this object
2336: * and is not a <code>AbstractParentObject</code>.
2337: * @param child the child object
2338: * @return <code>true</code> if specified child is a child of this object
2339: * and is not a <code>AbstractParentObject</code>
2340: * @throws DataAccessException if there is an error populating the list of children
2341: */
2342: private boolean isLeafChild(AbstractChildObject child)
2343: throws DataAccessException {
2344: //fill data member
2345: if (m_bIsChildrenPopulated == false) {
2346: try {
2347: populateChildrenFromDatabase();
2348: } catch (PopulateException e) {
2349: throw new DataAccessException(
2350: "Error occured populating kids:"
2351: + e.getLocalizedMessage());
2352: }
2353: }
2354:
2355: return (m_children.contains(child) && (child instanceof AbstractParentObject) == false);
2356: }
2357:
2358: /**
2359: * Populates this object's list of children from the database.
2360: *
2361: * @throws PopulateException if an error occurs populating list
2362: */
2363: private synchronized void populateChildrenFromDatabase()
2364: throws PopulateException {
2365: if (m_bIsChildrenPopulated == false) {
2366:
2367: if (m_logger.isLoggable(Level.FINER)) {
2368: m_logger.logp(Level.FINER, this .getClass().getName(),
2369: "populateChildrenFromDatabase",
2370: "populating children for parent, id - " + m_nId
2371: + ", key - " + m_nObjectKey);
2372: }
2373:
2374: if (isPopulated() == false) {
2375: populateFromDatabase();
2376: }
2377:
2378: if (m_children == null) {
2379: m_children = new Vector();
2380: }
2381:
2382: if (m_real_children == null) {
2383: m_real_children = new Vector();
2384: }
2385:
2386: List childClassNames = getChildClassNames();
2387:
2388: if ((this .m_nId != AbstractObject.NOTDBSAVED_ID)
2389: && childClassNames != null
2390: && childClassNames.size() > 0) {
2391: SelectStatement select = new SelectStatement();
2392:
2393: Iterator iter = childClassNames.iterator();
2394: while (iter.hasNext()) {
2395:
2396: ColumnRef idColref;
2397: ColumnRef typeColref;
2398: ColumnRef linkColref;
2399: try {
2400: String sChildClassName = (String) iter.next();
2401:
2402: String sChildTableName = null;
2403:
2404: try {
2405: sChildTableName = DatabaseInfo
2406: .getInstance().getTableName(
2407: sChildClassName);
2408: } catch (DataStoreException e) {
2409: throw new PopulateException(
2410: "Error getting table name", e);
2411: }
2412:
2413: idColref = getColumnRef(sChildClassName,
2414: ATTRIB_ID);
2415:
2416: linkColref = getGroupChildJoinColumnRef(
2417: sChildTableName,
2418: AbstractChildObject.CLMN_DEFAULT_LINK);
2419: typeColref = getColumnRef(sChildClassName,
2420: ATTRIB_TYPE);
2421:
2422: select.addSelectColumn(idColref); //1
2423: select.addSelectColumn(linkColref); //2
2424: select.addSelectColumn(typeColref); //3
2425: ColumnRef posCol = getGroupChildJoinColumnRef(
2426: sChildTableName, CLMN_POSITION);
2427: select.addSelectColumn(posCol); //4
2428:
2429: Collection coreRefs = AbstractParentObject
2430: .getCoreColumnRefs(sChildClassName);
2431: Iterator resIter = coreRefs.iterator();
2432:
2433: while (resIter.hasNext() == true) {
2434: select.addSelectColumn((ColumnRef) resIter
2435: .next());
2436: }
2437:
2438: select.setOrderBy(posCol);
2439:
2440: //join children to join table
2441: ColumnRef joinColumn1 = getColumnRef(
2442: sChildClassName,
2443: AbstractObject.ATTRIB_KEY);
2444: ColumnRef joinColumn2 = getGroupChildJoinColumnRef(
2445: sChildTableName, TAG_CHILDREN);
2446:
2447: select.addJoinCondition(joinColumn1,
2448: joinColumn2);
2449:
2450: ColumnRef whereColumn = getGroupChildJoinColumnRef(
2451: sChildTableName, TAG_GROUP);
2452:
2453: select.addWhereCondition(whereColumn, "=",
2454: m_nObjectKey);
2455: select.setOrderBy(joinColumn2);
2456: } catch (DataStoreException e) {
2457: throw new PopulateException(
2458: "Error occured building query", e);
2459: }
2460:
2461: ResultSet rs = null;
2462:
2463: try {
2464: rs = m_dsi.execute(select);
2465:
2466: int nId = 0;
2467:
2468: while (rs.next()) {
2469: nId = rs.getInt(idColref.getColumn());
2470:
2471: AbstractProfiledObject tmpChild = (AbstractProfiledObject) HarmoniseObjectFactory
2472: .instantiatePublishableObject(
2473: this .m_dsi,
2474: rs.getString(typeColref
2475: .getColumn()), nId);
2476:
2477: tmpChild.addEditEventListener(this );
2478:
2479: if (tmpChild.isPopulated() == false) {
2480: tmpChild.populateFromResultSetRow(rs,
2481: select);
2482: }
2483:
2484: OrderableCachePointer ptr = null;
2485:
2486: ptr = new OrderableCachePointer(
2487: CacheHandler.getInstance(m_dsi)
2488: .getCachePointer(tmpChild));
2489:
2490: int nIndex = rs.getInt(4);
2491:
2492: if (nIndex > m_nMaxIndex) {
2493: m_nMaxIndex = nIndex;
2494: }
2495:
2496: ptr.setPosition(nIndex);
2497:
2498: m_children.add(ptr);
2499:
2500: if (rs.getBoolean(linkColref.getColumn())) {
2501: m_real_children.add(new Integer(nId));
2502: }
2503: }
2504:
2505: //sort now so we don't have to do it all the time
2506: m_children = new ArrayList(
2507: getOrderedChildPointers());
2508: } catch (HarmoniseFactoryException e) {
2509: throw new PopulateException(
2510: "Error occured instantiating object from factory",
2511: e);
2512: } catch (DataStoreException e) {
2513: throw new PopulateException(
2514: "Error occured processing query", e);
2515: } catch (SQLException e) {
2516: throw new PopulateException("SQL error", e);
2517: } catch (CacheException e) {
2518: throw new PopulateException(
2519: "Error occured getting cache pointer",
2520: e);
2521: } finally {
2522: if (rs != null) {
2523: try {
2524: rs.close();
2525: } catch (SQLException e) {
2526: throw new PopulateException(
2527: "Error occured closing result set",
2528: e);
2529: }
2530: }
2531:
2532: m_bIsChildrenPopulated = true;
2533: }
2534: select.clear();
2535: }
2536:
2537: } else {
2538: m_bIsChildrenPopulated = true;
2539: }
2540: }
2541: }
2542:
2543: /**
2544: * Populates the list of archived children or descendants from the database.
2545: *
2546: * @param bAllChildren <code>true</code> if the archived descendant list
2547: * should be populated, otherwise the list of archived children will be populated
2548: * @throws PopulateException if an error occurs populating the list
2549: */
2550: private void populateArchivedChildrenFromDatabase(
2551: boolean bAllChildren) throws PopulateException {
2552:
2553: List archive = null;
2554:
2555: if (bAllChildren == true) {
2556: if (m_allArchivedChildren == null) {
2557: m_allArchivedChildren = new Vector();
2558: }
2559:
2560: archive = m_allArchivedChildren;
2561: } else {
2562: if (m_archivedChildren == null) {
2563: m_archivedChildren = new Vector();
2564: }
2565:
2566: archive = m_archivedChildren;
2567: }
2568:
2569: List children = null;
2570: try {
2571: children = getChildren();
2572: } catch (DataAccessException e) {
2573: throw new PopulateException(
2574: "Error occured getting children", e);
2575: }
2576:
2577: // get the constructor we need
2578: List classNames = this .getChildClassNames();
2579:
2580: Iterator iter = classNames.iterator();
2581:
2582: while (iter.hasNext()) {
2583:
2584: String sClassname = (String) iter.next();
2585: Class childClass = null;
2586: try {
2587: childClass = Class.forName(sClassname);
2588: } catch (ClassNotFoundException e) {
2589: throw new PopulateException(
2590: "Error occured getting class for child", e);
2591: }
2592:
2593: SelectStatement select = new SelectStatement();
2594:
2595: try {
2596: ColumnRef histIdColumn = AbstractObject.getColumnRef(
2597: sClassname, AbstractObject.ATTRIB_ID, true);
2598:
2599: select.addSelectColumn(histIdColumn);
2600: select.addSelectColumn(AbstractObject.getColumnRef(
2601: sClassname, AbstractObject.TAG_NAME, true));
2602: select.addSelectColumn(AbstractEditableObject
2603: .getColumnRef(sClassname,
2604: AbstractEditableObject.TAG_VERSION,
2605: true));
2606: select
2607: .addSelectColumn(AbstractEditableObject
2608: .getColumnRef(
2609: sClassname,
2610: AbstractEditableObject.TAG_VERSION_COMMENT,
2611: true));
2612: select
2613: .addSelectColumn(AbstractEditableObject
2614: .getColumnRef(
2615: sClassname,
2616: AbstractEditableObject.TAG_VERSION_DATE,
2617: true));
2618: select.addSelectColumn(AbstractEditableObject
2619: .getColumnRef(sClassname,
2620: AbstractObject.ATTRIB_KEY, true));
2621:
2622: ColumnRef whereColumn = AbstractChildObject
2623: .getColumnRef(sClassname,
2624: AbstractChildObject.TAG_PATH, true);
2625:
2626: if (bAllChildren) {
2627: select.addWhereCondition(whereColumn, "LIKE",
2628: getFullPath() + "%");
2629: } else {
2630: select.addWhereCondition(whereColumn, "=",
2631: getFullPath());
2632: }
2633:
2634: ColumnRef liveIdColumn = AbstractObject.getColumnRef(
2635: sClassname, ATTRIB_ID);
2636:
2637: //hist id not in live id list
2638: SelectStatement nestedSelect = new SelectStatement();
2639:
2640: nestedSelect.addSelectColumn(liveIdColumn);
2641:
2642: select.addWhereCondition(histIdColumn, "not in",
2643: nestedSelect);
2644:
2645: ColumnRef parentIdColumn = AbstractEditableObject
2646: .getColumnRef(sClassname, TAG_LIVE_VERSION,
2647: false);
2648:
2649: //hist id not in live parent_id list
2650: SelectStatement nestedSelect2 = new SelectStatement();
2651:
2652: nestedSelect2.addSelectColumn(parentIdColumn);
2653:
2654: select.addWhereCondition(histIdColumn, "not in",
2655: nestedSelect2);
2656:
2657: select.addOrderBy(AbstractObject.getColumnRef(
2658: sClassname, AbstractObject.ATTRIB_KEY, true),
2659: SelectStatement.ORDER_DESCENDING);
2660:
2661: } catch (DataAccessException e) {
2662: throw new PopulateException(
2663: "Error occured accessing data", e);
2664: } catch (DataStoreException e) {
2665: throw new PopulateException(
2666: "Error occured building query", e);
2667: }
2668:
2669: ResultSet rs = null;
2670:
2671: try {
2672: rs = m_dsi.execute(select);
2673:
2674: Vector ids = new Vector();
2675:
2676: while (rs.next()) {
2677: int nId = rs.getInt(1);
2678: Integer intId = new Integer(nId);
2679:
2680: if (ids.contains(intId) == false) {
2681: AbstractChildObject obj = (AbstractChildObject) childClass
2682: .newInstance();
2683:
2684: obj.setDataStoreInterface(m_dsi);
2685: obj.setId(nId);
2686: obj.setHistorical(true);
2687:
2688: obj.populateFromResultSetRow(rs, select);
2689:
2690: obj.setRealParent(this );
2691: obj.addEditEventListener(this );
2692:
2693: archive.add(obj);
2694: ids.add(intId);
2695: }
2696: }
2697: } catch (SQLException e) {
2698: throw new PopulateException("SQL error", e);
2699: } catch (IllegalArgumentException e) {
2700: throw new PopulateException("Illegal argument error", e);
2701: } catch (InstantiationException e) {
2702: throw new PopulateException("Instantiation error", e);
2703: } catch (IllegalAccessException e) {
2704: throw new PopulateException("Illegal access error", e);
2705: } catch (DataStoreException e) {
2706: throw new PopulateException(
2707: "Error occured processing query", e);
2708: } finally {
2709: if (rs != null) {
2710: try {
2711: rs.close();
2712: } catch (SQLException e) {
2713: throw new PopulateException(
2714: "Error occured closing result set", e);
2715: }
2716: }
2717:
2718: if (bAllChildren) {
2719: m_bIsAllArchivedChildrenPopulated = true;
2720: } else {
2721: m_bIsArchivedChildrenPopulated = true;
2722: }
2723: }
2724:
2725: }
2726: }
2727:
2728: /* (non-Javadoc)
2729: * @see org.openharmonise.rm.resources.lifecycle.EditEventListener#workflowObjectArchived(org.openharmonise.rm.resources.lifecycle.EditEvent)
2730: */
2731: public void workflowObjectArchived(EditEvent event) {
2732: Editable source = (Editable) event.getSource();
2733:
2734: //if source is a child act accordingly
2735: if (source instanceof AbstractChildObject) {
2736: AbstractChildObject childSource = (AbstractChildObject) source;
2737:
2738: //if child is a valid child of this parent clear all archived children lists
2739: //so that they can be repopulated if necessary
2740: //Note: there is no way to check whether it's valid to add the result
2741: //of the event to the archived lists as the original child will no longer
2742: //be a child of the parent
2743: if (isValidChild(childSource) == true) {
2744:
2745: if (m_bIsArchivedChildrenPopulated == true) {
2746: m_bIsArchivedChildrenPopulated = false;
2747: m_archivedChildren.clear();
2748: }
2749:
2750: if (m_bIsAllArchivedChildrenPopulated == true) {
2751: m_bIsAllArchivedChildrenPopulated = false;
2752: m_allArchivedChildren.clear();
2753: }
2754: }
2755:
2756: }
2757:
2758: super .workflowObjectArchived(event);
2759:
2760: }
2761:
2762: /* (non-Javadoc)
2763: * @see org.openharmonise.rm.resources.lifecycle.EditEventListener#workflowObjectLocked(org.openharmonise.rm.resources.lifecycle.EditEvent)
2764: */
2765: public void workflowObjectLocked(EditEvent event) {
2766: super .workflowObjectLocked(event);
2767: }
2768:
2769: /* (non-Javadoc)
2770: * @see org.openharmonise.rm.resources.lifecycle.EditEventListener#workflowObjectReactivated(org.openharmonise.rm.resources.lifecycle.EditEvent)
2771: */
2772: public void workflowObjectReactivated(EditEvent event) {
2773: Object src = event.getSource();
2774:
2775: //if source of event has been archived it's no longer
2776: //valid it to have listed in the archive
2777: if (src instanceof AbstractChildObject) {
2778:
2779: if (m_bIsAllArchivedChildrenPopulated == true) {
2780: m_allArchivedChildren.remove(src);
2781: }
2782:
2783: if (m_bIsArchivedChildrenPopulated == true) {
2784: m_archivedChildren.remove(src);
2785: }
2786: }
2787:
2788: super .workflowObjectReactivated(event);
2789: }
2790:
2791: /* (non-Javadoc)
2792: * @see org.openharmonise.rm.resources.lifecycle.EditEventListener#workflowObjectSaved(org.openharmonise.rm.resources.lifecycle.EditEvent)
2793: */
2794: public void workflowObjectSaved(EditEvent event) {
2795: super .workflowObjectSaved(event);
2796: }
2797:
2798: /* (non-Javadoc)
2799: * @see org.openharmonise.rm.resources.lifecycle.EditEventListener#workflowObjectStatusChanged(org.openharmonise.rm.resources.lifecycle.EditEvent)
2800: */
2801: public void workflowObjectStatusChanged(EditEvent event) {
2802:
2803: super .workflowObjectStatusChanged(event);
2804: }
2805:
2806: /* (non-Javadoc)
2807: * @see org.openharmonise.rm.resources.lifecycle.EditEventListener#workflowObjectUnlocked(org.openharmonise.rm.resources.lifecycle.EditEvent)
2808: */
2809: public void workflowObjectUnlocked(EditEvent event) {
2810: super .workflowObjectUnlocked(event);
2811:
2812: }
2813:
2814: /**
2815: * Subclass of <code>CachePointer</code> which stores position data,
2816: * allowing an orderable list to be created and maintained.
2817: *
2818: * @author Michael Bell
2819: * @version $Revision: 1.6.2.1 $
2820: *
2821: */
2822: private class OrderableCachePointer extends CachePointer {
2823:
2824: /**
2825: * Index of this cache pointer in list
2826: */
2827: private int m_nPosition = -1;
2828:
2829: /**
2830: * Constructs a cache pointer which does not reference any object
2831: */
2832: public OrderableCachePointer() {
2833: super ();
2834: }
2835:
2836: /**
2837: * Constructs an orderable cache pointer based in the specified
2838: * <code>CachePointer</code>
2839: *
2840: * @param ptr the <code>CachePointer</code> to base this object on
2841: */
2842: public OrderableCachePointer(CachePointer ptr) {
2843: this .setCache(ptr.getCache());
2844: this .setKey(ptr.getKey());
2845: }
2846:
2847: /**
2848: * Constructs a cache pointer which references the object found in
2849: * cache <code>cache</code> with cache key <code>key</code>.
2850: *
2851: * @param key the cache key
2852: * @param cache the cache
2853: */
2854: public OrderableCachePointer(Object key, AbstractCache cache) {
2855: super (key, cache);
2856: }
2857:
2858: /**
2859: * Returns the index of this cache pointer
2860: *
2861: * @return the index of this cache pointer
2862: */
2863: public int getPosition() {
2864: return m_nPosition;
2865: }
2866:
2867: /**
2868: * Sets the index of this cache pointer
2869: *
2870: * @param i the index of this cache pointer
2871: */
2872: public void setPosition(int i) {
2873: m_nPosition = i;
2874: }
2875:
2876: /* (non-Javadoc)
2877: * @see java.lang.Object#toString()
2878: */
2879: public String toString() {
2880: String result = null;
2881:
2882: StringBuffer strbuf = new StringBuffer();
2883:
2884: try {
2885: strbuf.append(
2886: ((AbstractChildObject) getObject()).getName())
2887: .append(" ").append(m_nPosition);
2888: result = strbuf.toString();
2889: } catch (DataAccessException e) {
2890: m_logger.log(Level.WARNING, e.getLocalizedMessage(), e);
2891: } catch (CacheException e) {
2892: m_logger.log(Level.WARNING, e.getLocalizedMessage(), e);
2893: }
2894:
2895: return result;
2896: }
2897:
2898: /**
2899: * Returns a cache pointer which references the given
2900: * child object
2901: *
2902: * @param child the child object
2903: * @return an cache pointer
2904: * @throws PopulateException if there is an error populating the list of
2905: * children
2906: * @throws CacheException if there is an error accessing the object held
2907: * in an orderable cache pointer
2908: */
2909: public CachePointer getCachePointer() {
2910: return new CachePointer(this .getKey(), this .getCache());
2911: }
2912:
2913: /* (non-Javadoc)
2914: * @see java.lang.Object#equals(java.lang.Object)
2915: */
2916: public boolean equals(Object obj) {
2917: boolean bEq = false;
2918:
2919: if (obj instanceof CachePointer) {
2920: CachePointer ptr = (CachePointer) obj;
2921:
2922: if (this == ptr) {
2923: bEq = true;
2924: } else if (ptr.getCache().equals(m_cache) == true
2925: && ptr.getKey().equals(m_cache_key) == true) {
2926: bEq = true;
2927:
2928: if (ptr instanceof OrderableCachePointer
2929: && bEq == true
2930: && ((OrderableCachePointer) ptr).m_nPosition != this .m_nPosition) {
2931: bEq = false;
2932: }
2933: }
2934: }
2935:
2936: return bEq;
2937: }
2938:
2939: }
2940:
2941: /**
2942: *
2943: * Comparator class for <code>OrderableCachePointer</code> sorting
2944: *
2945: * @author Michael Bell
2946: * @version $Revision: 1.6.2.1 $
2947: *
2948: */
2949: private class OrderableCachePointerComparator implements Comparator {
2950:
2951: /* (non-Javadoc)
2952: * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
2953: */
2954: public int compare(Object arg1, Object arg2) {
2955: OrderableCachePointer ptr1 = (OrderableCachePointer) arg1;
2956: OrderableCachePointer ptr2 = (OrderableCachePointer) arg2;
2957:
2958: int nCompare = 0;
2959: int nPos1 = ptr1.getPosition();
2960: int nPos2 = ptr2.getPosition();
2961:
2962: if (nPos1 != nPos2) {
2963:
2964: //makre sure that any index of -1 gets pushed to the end
2965: //of the list
2966: if (nPos2 == -1) {
2967: nCompare = 1;
2968: } else {
2969: nCompare = nPos1 - nPos2;
2970: }
2971:
2972: } else {
2973:
2974: try {
2975: AbstractChildObject child1 = (AbstractChildObject) ptr1
2976: .getObject();
2977: AbstractChildObject child2 = (AbstractChildObject) ptr2
2978: .getObject();
2979:
2980: nCompare = child1.compareTo(child2);
2981: } catch (CacheException e) {
2982: m_logger.log(Level.WARNING,
2983: e.getLocalizedMessage(), e);
2984: nCompare = 0;
2985: }
2986: }
2987:
2988: return nCompare;
2989: }
2990:
2991: }
2992:
2993: /**
2994: * Returns an ordered set of this object's children
2995: *
2996: * @return an ordered set of this object's children
2997: */
2998: private Set getOrderedChildPointers() {
2999: TreeSet sortedSet = new TreeSet(
3000: new OrderableCachePointerComparator());
3001: sortedSet.addAll(m_children);
3002:
3003: return sortedSet;
3004: }
3005:
3006: /**
3007: * Returns an orderable cache pointer which references the given
3008: * child object.
3009: *
3010: * @param child the child object
3011: * @return an orderable cache pointer
3012: * @throws PopulateException if there is an error populating the list of
3013: * children
3014: * @throws CacheException if there is an error accessing the object held
3015: * in an orderable cache pointer
3016: */
3017: private OrderableCachePointer getCachePointer(
3018: AbstractChildObject child) throws PopulateException,
3019: CacheException {
3020: if (m_bIsChildrenPopulated == false) {
3021: populateChildrenFromDatabase();
3022: }
3023: OrderableCachePointer result = null;
3024: Iterator iter = m_children.iterator();
3025: boolean bFound = false;
3026: while (iter.hasNext() && bFound == false) {
3027: OrderableCachePointer ptr = (OrderableCachePointer) iter
3028: .next();
3029:
3030: if (((AbstractChildObject) ptr.getObject()).getId() == child
3031: .getId()
3032: && ((AbstractChildObject) ptr.getObject())
3033: .getClass().equals(child.getClass())) {
3034: bFound = true;
3035: result = ptr;
3036: }
3037: }
3038:
3039: return result;
3040: }
3041:
3042: /* (non-Javadoc)
3043: * @see org.openharmonise.rm.dsi.DataStoreObject#getInstanceColumnRef(java.lang.String, boolean)
3044: */
3045: public ColumnRef getInstanceColumnRef(String sColumn,
3046: boolean bIsHist) throws DataStoreException {
3047: ColumnRef returnColRef = null;
3048:
3049: if (sColumn.equals(TAG_SUBGROUPS) == true) {
3050: returnColRef = this
3051: .getParentChildJoinColumnRef(TAG_CHILDREN);
3052: } else if (sColumn.equals(TAG_GROUP) == true) {
3053: returnColRef = this.getParentChildJoinColumnRef(sColumn);
3054: }
3055:
3056: if (returnColRef != null) {
3057: return returnColRef;
3058: } else {
3059: return super.getInstanceColumnRef(sColumn, bIsHist);
3060: }
3061: }
3062:
3063: }
|