001: /***************************************************************
002: * This file is part of the [fleXive](R) project.
003: *
004: * Copyright (c) 1999-2008
005: * UCS - unique computing solutions gmbh (http://www.ucs.at)
006: * All rights reserved
007: *
008: * The [fleXive](R) project is free software; you can redistribute
009: * it and/or modify it under the terms of the GNU General Public
010: * License as published by the Free Software Foundation;
011: * either version 2 of the License, or (at your option) any
012: * later version.
013: *
014: * The GNU General Public License can be found at
015: * http://www.gnu.org/copyleft/gpl.html.
016: * A copy is found in the textfile GPL.txt and important notices to the
017: * license from the author are found in LICENSE.txt distributed with
018: * these libraries.
019: *
020: * This library is distributed in the hope that it will be useful,
021: * but WITHOUT ANY WARRANTY; without even the implied warranty of
022: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
023: * GNU General Public License for more details.
024: *
025: * For further information about UCS - unique computing solutions gmbh,
026: * please see the company website: http://www.ucs.at
027: *
028: * For further information about [fleXive](R), please see the
029: * project website: http://www.flexive.org
030: *
031: *
032: * This copyright notice MUST APPEAR in all copies of the file!
033: ***************************************************************/package com.flexive.shared.content;
034:
035: import com.flexive.shared.CacheAdmin;
036: import com.flexive.shared.XPathElement;
037: import com.flexive.shared.exceptions.FxCreateException;
038: import com.flexive.shared.exceptions.FxInvalidParameterException;
039: import com.flexive.shared.exceptions.FxNotFoundException;
040: import com.flexive.shared.structure.FxAssignment;
041: import com.flexive.shared.structure.FxMultiplicity;
042: import com.flexive.shared.structure.FxType;
043: import org.apache.commons.lang.ArrayUtils;
044:
045: import java.io.Serializable;
046: import java.util.ArrayList;
047: import java.util.List;
048:
049: /**
050: * Abstract base class for property and group data
051: *
052: * @author Markus Plesser (markus.plesser@flexive.com), UCS - unique computing solutions gmbh (http://www.ucs.at)
053: */
054: public abstract class FxData implements Serializable {
055: private static final long serialVersionUID = 1268634970583396012L;
056:
057: public final static int POSITION_TOP = -1;
058: public final static int POSITION_BOTTOM = -2;
059:
060: /**
061: * XPath without indices
062: */
063: private String XPath;
064:
065: /**
066: * XPath with indices
067: */
068: protected String XPathFull;
069:
070: /**
071: * Indices for each element in the XPath
072: */
073: protected int[] indices;
074:
075: /**
076: * Id of the associated assignment
077: */
078: private long assignmentId;
079:
080: /**
081: * Multiplicity of the assignment
082: */
083: private FxMultiplicity assignmentMultiplicity;
084:
085: /**
086: * Position within same hierarchy level
087: */
088: private int pos;
089:
090: /**
091: * Parent element (virtual root for the first)
092: */
093: private FxGroupData parent;
094:
095: /**
096: * Is this a system internal entry?
097: */
098: private boolean systemInternal;
099:
100: /**
101: * XPathElement of this entry
102: */
103: protected XPathElement xp;
104:
105: /**
106: * xXPath prefix like "FxType name[@pk=..]"
107: */
108: protected String xpPrefix;
109:
110: protected FxData(String xpPrefix, String alias, int index,
111: String xPath, String xPathFull, int[] indices,
112: long assignmentId, FxMultiplicity assignmentMultiplicity,
113: int pos, FxGroupData parent, boolean systemInternal)
114: throws FxInvalidParameterException {
115: this .xpPrefix = xpPrefix;
116: this .XPath = XPathElement.stripType(xPath);
117: this .XPathFull = XPathElement.stripType(xPathFull);
118: this .indices = indices;
119: this .assignmentId = assignmentId;
120: this .assignmentMultiplicity = assignmentMultiplicity;
121: this .pos = pos;
122: this .parent = parent;
123: this .xp = new XPathElement(alias, index, true);
124: if (index != 1)
125: applyIndices();
126: this .systemInternal = systemInternal;
127: }
128:
129: /**
130: * Is this FxData a property or group?
131: *
132: * @return if this FxData is a property or group
133: */
134: public abstract boolean isProperty();
135:
136: /**
137: * Is this FxData a property or group?
138: *
139: * @return if this FxData is a property or group
140: */
141: public abstract boolean isGroup();
142:
143: /**
144: * Is this data empty?
145: *
146: * @return empty
147: */
148: public abstract boolean isEmpty();
149:
150: /**
151: * Is this a system internal property or group?
152: *
153: * @return system internal
154: */
155: public boolean isSystemInternal() {
156: return systemInternal;
157: }
158:
159: public int getIndex() {
160: return xp.getIndex();
161: }
162:
163: /**
164: * Set the index of this data entry
165: *
166: * @param index the index to set
167: */
168: private void setIndex(int index) {
169: this .xp.setIndex(index);
170: this .applyIndices();
171: }
172:
173: /**
174: * Apply the multiplicity to XPath and children if its a group
175: */
176: protected abstract void applyIndices();
177:
178: /**
179: * Get the prefix to use for the XPath (Name of the type and primary key)
180: *
181: * @return prefix to use for the XPath
182: */
183: public String getXPathPrefix() {
184: return xpPrefix;
185: }
186:
187: /**
188: * Get the XPath of this FxData
189: *
190: * @return XPath
191: */
192: public String getXPath() {
193: return XPath;
194: }
195:
196: /**
197: * Get this FxData as XPathElement
198: *
199: * @return XPathElement
200: */
201: public XPathElement getXPathElement() {
202: return xp;
203: }
204:
205: /**
206: * Get this FxData's XPath with all indices
207: *
208: * @return XPath with all indices
209: */
210: public String getXPathFull() {
211: return XPathFull;
212: }
213:
214: /**
215: * Get the indices of this FxData (min, max)
216: *
217: * @return indices of this FxData (min, max)
218: */
219: public int[] getIndices() {
220: return ArrayUtils.clone(indices);
221: }
222:
223: /**
224: * Get the id of the assignment of this FxData
225: *
226: * @return assignment id
227: */
228: public long getAssignmentId() {
229: return assignmentId;
230: }
231:
232: /**
233: * Get this FxData's associated assignment
234: *
235: * @return FxAssignment
236: */
237: public FxAssignment getAssignment() {
238: return CacheAdmin.getEnvironment().getAssignment(
239: this .getAssignmentId());
240: }
241:
242: /**
243: * May more instances of this element be created within the same hierarchy level
244: *
245: * @return if more instances of this element may be created within the same hierarchy level
246: */
247: public boolean mayCreateMore() {
248: return getCreateableElements() > 0;
249: }
250:
251: /**
252: * Get the number of (same) FxData elements that may be created within the same hierarchy level
253: *
254: * @return number of (same) FxData elements that may be created within the same hierarchy level
255: */
256: public int getCreateableElements() {
257: if (parent == null)
258: return 0; //just one instance of the virtual root group
259: if (assignmentMultiplicity.getMax() == 1)
260: return 0; //we have to be the only instance
261: if (assignmentMultiplicity.isUnlimited())
262: return FxMultiplicity.N;
263: //gather count of same elements
264: int count = getOccurances();
265: count = assignmentMultiplicity.getMax() - count;
266: return count > 0 ? count : 0;
267: }
268:
269: /**
270: * Return the number of occurances of this assignment in this
271: * FxData instance.
272: *
273: * @return the number of occurances of this assignment in this FxData instance
274: */
275: public int getOccurances() {
276: return getElements().size();
277: }
278:
279: public List<FxData> getElements() {
280: List<FxData> elements = new ArrayList<FxData>();
281: for (FxData data : parent.getChildren()) {
282: if (data.getAssignmentId() == assignmentId) {
283: elements.add(data);
284: }
285: }
286: return elements;
287: }
288:
289: /**
290: * Get the number of elements that may be removed within the same hierarchy level
291: *
292: * @return number of elements that may be removed within the same hierarchy level
293: */
294: public int getRemoveableElements() {
295: if (parent == null)
296: return 0; //just one instance of the virtual root group
297: //gather count of same elements
298: int count = 0;
299: for (FxData curr : parent.getChildren())
300: if (curr.getAssignmentId() == this .assignmentId
301: && !curr.isSystemInternal())
302: count++;
303: count -= assignmentMultiplicity.getMin();
304: return count > 0 ? count : 0;
305: }
306:
307: /**
308: * Compact the indices of assignments from the same type closing gaps, etc
309: */
310: public void compact() {
311: if (parent == null)
312: return; //cant compact root group
313: int idx = 1;
314: int pos = 0;
315: boolean foundOther = false;
316: for (FxData curr : parent.getChildren()) {
317: curr.setPos(pos++);
318: if (curr.getAssignmentId() == this .assignmentId) {
319: curr.setIndex(idx++);
320: foundOther = true;
321: }
322: }
323: if (!foundOther && this .getIndex() > 1)
324: this .setIndex(1);
325: }
326:
327: /**
328: * Create a new instance of this FxData with the next available multiplicity at the requested position
329: *
330: * @param insertPosition the requested inserting position
331: * @return a new FxData object
332: * @throws FxNotFoundException on errors
333: * @throws FxInvalidParameterException on errors
334: * @throws FxCreateException on errors
335: */
336: public synchronized FxData createNew(int insertPosition)
337: throws FxNotFoundException, FxInvalidParameterException,
338: FxCreateException {
339: if (!mayCreateMore())
340: throw new FxCreateException(
341: "ex.content.data.create.maxMultiplicity", this
342: .getXPath(), this
343: .getAssignmentMultiplicity().getMax());
344: FxType type = CacheAdmin.getEnvironment().getAssignment(
345: assignmentId).getAssignedType();
346: if (insertPosition == POSITION_BOTTOM) {
347: if (parent.getChildren().size() == 0)
348: insertPosition = 0; //should not be possible to happen but play safe ...
349: else
350: insertPosition = parent.getChildren().get(
351: parent.getChildren().size() - 1).getPos() + 1;
352: } else if (insertPosition < 0 || insertPosition == POSITION_TOP)
353: insertPosition = 0;
354: compact();
355: int newIndex = 1;
356: boolean movePos = false;
357: for (FxData curr : parent.getChildren()) {
358: if (curr.getAssignmentId() == this .assignmentId)
359: newIndex = curr.getIndex() + 1;
360: if (curr.getPos() == insertPosition)
361: movePos = true;
362: if (movePos)
363: curr.setPos(curr.getPos() + 1);
364: }
365: FxAssignment fxa = type.getAssignment(this .getXPath());
366: FxData newData = fxa.createEmptyData(parent, newIndex);
367: newData.setPos(insertPosition);
368: newData.applyIndices();
369: parent.addChild(newData); //adding honors the position!
370: return newData;
371: }
372:
373: /**
374: * Get the multiplicity of the associated assignment
375: *
376: * @return multiplicity of the associated assignment
377: */
378: public FxMultiplicity getAssignmentMultiplicity() {
379: return assignmentMultiplicity;
380: }
381:
382: /**
383: * Get the position of this data element within its hierarchy level
384: *
385: * @return position of this data element within its hierarchy level
386: */
387: public int getPos() {
388: return pos;
389: }
390:
391: /**
392: * Set the position of this data element within its hierarchy level
393: *
394: * @param pos position to set
395: * @return this
396: */
397: public FxData setPos(int pos) {
398: this .pos = pos;
399: return this ;
400: }
401:
402: /**
403: * Get the parent group of this data element
404: *
405: * @return parent group of this data element
406: */
407: public FxGroupData getParent() {
408: return parent;
409: }
410:
411: /**
412: * Getter for the XPath alias (current XPath element name)
413: *
414: * @return alias
415: */
416: public String getAlias() {
417: return xp.getAlias();
418: }
419:
420: /**
421: * Is this data removeable?
422: *
423: * @return if this data is removeable
424: */
425: public boolean isRemoveable() {
426: return this .getRemoveableElements() > 0;
427: }
428:
429: /**
430: * {@inheritDoc}
431: */
432: @Override
433: public String toString() {
434: return this .getXPathFull();
435: }
436:
437: /**
438: * {@inheritDoc}
439: */
440: @Override
441: public boolean equals(Object obj) {
442: if (this == obj)
443: return true;
444: if (!(obj instanceof FxData))
445: return false;
446: FxData comp = (FxData) obj;
447: return this .getPos() == comp.getPos()
448: && !(!this .getXPathFull().equals(comp.getXPathFull())
449: || this .getAssignmentId() != comp
450: .getAssignmentId() || this .isEmpty() != comp
451: .isEmpty());
452: }
453:
454: @Override
455: public int hashCode() {
456: int result;
457: result = XPathFull.hashCode();
458: result = 31 * result
459: + (int) (assignmentId ^ (assignmentId >>> 32));
460: return result;
461: }
462:
463: /**
464: * Create an independent copy of this group or property FxData
465: *
466: * @param parent parent group
467: * @return independent copy
468: */
469: abstract FxData copy(FxGroupData parent);
470: }
|