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.structure;
034:
035: import com.flexive.shared.content.FxData;
036: import com.flexive.shared.content.FxGroupData;
037: import com.flexive.shared.exceptions.FxCreateException;
038: import com.flexive.shared.exceptions.FxNotFoundException;
039: import com.flexive.shared.scripting.FxScriptEvent;
040: import com.flexive.shared.scripting.FxScriptMapping;
041: import com.flexive.shared.scripting.FxScriptMappingEntry;
042: import com.flexive.shared.value.FxString;
043:
044: import java.io.Serializable;
045: import java.util.*;
046:
047: /**
048: * Base class for assignments of a group or property to a type or another group/property
049: *
050: * @author Markus Plesser (markus.plesser@flexive.com), UCS - unique computing solutions gmbh (http://www.ucs.at)
051: */
052: public abstract class FxAssignment implements Serializable,
053: Comparable<FxAssignment> {
054: private static final long serialVersionUID = -6127833297182838935L;
055:
056: /**
057: * Empty list returned if no script mapping is defined for a FxScriptEvent
058: */
059: private final static long[] EMPTY_SCRIPTMAPPING = new long[0];
060:
061: /**
062: * parent id value if an assignment has no parent
063: */
064: public static final long NO_PARENT = 0;
065:
066: /**
067: * base id value if an assignment belongs to the virtual root type
068: */
069: public static final long ROOT_BASE = 0;
070:
071: /**
072: * Constant to determine type of the assignment in database; group
073: */
074: public final static int TYPE_GROUP = 0;
075:
076: /**
077: * Constant to determine type of the assignment in database; property
078: */
079: public final static int TYPE_PROPERTY = 1;
080:
081: /**
082: * Internal id of this assignment
083: */
084: private long assignmentId;
085:
086: /**
087: * Is this assignment enabled at all?
088: * Disabled assignments will only show up in the admin area for
089: * structure and will be hidden from all other areas
090: */
091: protected boolean enabled;
092:
093: /**
094: * FxType this assignment belongs to
095: */
096: private FxType assignedType;
097:
098: /**
099: * Multiplicity of this assignment
100: */
101: protected FxMultiplicity multiplicity;
102:
103: /**
104: * Default multiplicity, will be auto adjusted if < min or > max
105: */
106: protected int defaultMultiplicity;
107:
108: /**
109: * Position of this assignment within the same XPath hierarchy
110: */
111: protected int position;
112:
113: /**
114: * Absolute XPath without indices from the base FxType
115: */
116: protected String XPath;
117:
118: /**
119: * (optional) alias, if not defined the name of the assigned element
120: */
121: protected String alias;
122:
123: /**
124: * (optional) parent group
125: */
126: protected FxGroupAssignment parentGroupAssignment;
127:
128: /**
129: * base assignment (if derived the parent, if not the root assignment, if its a root assignment FxAssignment.ROOT_BASE)
130: */
131: private long baseAssignment;
132:
133: /**
134: * (optional) description
135: */
136: protected FxString label;
137:
138: /**
139: * (optional) hint
140: */
141: protected FxString hint;
142: /**
143: * Script mapping, is resolved while loading the environment
144: */
145: protected Map<FxScriptEvent, long[]> scriptMapping;
146:
147: private boolean systemInternal;
148: List<FxStructureOption> options;
149:
150: /**
151: * Constructor
152: *
153: * @param assignmentId internal id of this assignment
154: * @param enabled is this assignment enabled?
155: * @param assignedType the FxType this assignment belongs to
156: * @param alias an optional alias, if <code>null</code> the original name will be used
157: * @param xpath absolute XPath without indices from the base FxType
158: * @param position position within the same XPath hierarchy
159: * @param multiplicity multiplicity
160: * @param defaultMultiplicity default multiplicity
161: * @param parentGroupAssignment (optional) parent FxGroupAssignment this assignment belongs to
162: * @param baseAssignment base assignment (if derived the parent, if not the root assignment, if its a root assignment FxAssignment.ROOT_BASE)
163: * @param label (optional) label
164: * @param hint (optional) hint
165: * @param options options
166: */
167: protected FxAssignment(long assignmentId, boolean enabled,
168: FxType assignedType, String alias, String xpath,
169: int position, FxMultiplicity multiplicity,
170: int defaultMultiplicity,
171: FxGroupAssignment parentGroupAssignment,
172: long baseAssignment, FxString label, FxString hint,
173: List<FxStructureOption> options) {
174: this .alias = (alias != null ? alias.toUpperCase() : null);
175: this .assignedType = assignedType;
176: this .assignmentId = assignmentId;
177: this .multiplicity = multiplicity;
178: this .defaultMultiplicity = defaultMultiplicity;
179: this .enabled = enabled;
180: this .position = position;
181: this .parentGroupAssignment = parentGroupAssignment;
182: this .baseAssignment = baseAssignment;
183: this .XPath = xpath;
184: this .label = label;
185: this .hint = hint;
186: this .systemInternal = false;
187: this .options = options;
188: if (this .options == null)
189: this .options = FxStructureOption.getEmptyOptionList(2);
190: }
191:
192: /**
193: * Get the alias of this assignment.
194: * Groups and properties may define an alias to allow multiple use of the same group or property but
195: * using a different name.
196: * An alias is always defined, if not explicitly it is the properties/groups name.
197: *
198: * @return alias of this assignment
199: */
200: public String getAlias() {
201: return alias;
202: }
203:
204: /**
205: * The internal id of this assignment as stored in the database
206: *
207: * @return internal id of this assignment
208: */
209: public long getId() {
210: return assignmentId;
211: }
212:
213: /**
214: * Is this assignment enabled?
215: *
216: * @return enabled
217: */
218: public boolean isEnabled() {
219: return enabled;
220: }
221:
222: /**
223: * Returns if this assignment is the child of another assignment or if it is directly attached to a FxType
224: *
225: * @return parent assignment
226: */
227: public boolean hasParentGroupAssignment() {
228: return getParentGroupAssignment() != null;
229: }
230:
231: /**
232: * If this assignment is assigned to a group, the assignment of the parent group (in the context of the current type)
233: *
234: * @return parent group assignment of this assignment
235: */
236: public FxGroupAssignment getParentGroupAssignment() {
237: return parentGroupAssignment;
238: }
239:
240: /**
241: * base assignment (if derived the parent, if not the root assignment, if its a root assignment FxAssignment.ROOT_BASE)
242: *
243: * @return base assignment
244: * @see FxAssignment#ROOT_BASE
245: */
246: public long getBaseAssignmentId() {
247: return baseAssignment;
248: }
249:
250: /**
251: * Returns true if the assignment is derived from a supertype.
252: *
253: * @return true if the assignment is derived from a supertype.
254: */
255: public boolean isDerivedAssignment() {
256: return baseAssignment != FxAssignment.ROOT_BASE;
257: }
258:
259: /**
260: * The FxType this assignment is associated with
261: *
262: * @return FxType this assignment is associated with
263: */
264: public FxType getAssignedType() {
265: return assignedType;
266: }
267:
268: /**
269: * Get the XPath of this assignment without indices
270: *
271: * @return XPath of this assignment without indices
272: */
273: public synchronized String getXPath() {
274: if (XPath != null)
275: return XPath;
276: //build XPath
277: StringBuffer sbXPath = new StringBuffer(200);
278: sbXPath.append(this .getAlias());
279: FxAssignment parent = this .getParentGroupAssignment();
280: while (parent != null) {
281: sbXPath.insert(0, parent.getAlias() + "/");
282: parent = parent.getParentGroupAssignment();
283: }
284: sbXPath.insert(0, getAssignedType().getName() + "/");
285: XPath = sbXPath.toString();
286: return XPath;
287: }
288:
289: /**
290: * Get the multiplicity of this assignment.
291: * Depending on if the assigned element allows overriding of its base multiplicity the base
292: * elements multiplicity is returned or the multiplicity of the assignment
293: *
294: * @return multiplicity of this assignment
295: */
296: public abstract FxMultiplicity getMultiplicity();
297:
298: /**
299: * Get the default multiplicity (used i.e. in user interfaces editors and determines the amount of values that will
300: * be initialized when creating an empty element).
301: * <p/>
302: * If the set value is < min or > max multiplicity of this assignment it will
303: * be auto adjusted to the next valid value without throwing an exception
304: *
305: * @return default multiplicity
306: */
307: public int getDefaultMultiplicity() {
308: FxMultiplicity m = this .getMultiplicity();
309: if (m.isValid(defaultMultiplicity))
310: return defaultMultiplicity;
311: if (defaultMultiplicity < m.getMin())
312: return m.getMin();
313: if (defaultMultiplicity > m.getMax())
314: return m.getMax();
315: return m.getMin();
316: }
317:
318: /**
319: * Get the position within the current XPath hierarchy
320: *
321: * @return position within the current XPath hierarchy
322: */
323: public int getPosition() {
324: return position;
325: }
326:
327: /**
328: * Get the assignment label
329: *
330: * @return the localized label of this assignment
331: */
332: public FxString getLabel() {
333: return label;
334: }
335:
336: /**
337: * Return a human-readable string to present this assignment to the user.
338: *
339: * @return the assignment's name as it should be displayed to the user
340: */
341: public String getDisplayName() {
342: return getDisplayLabel().getBestTranslation();
343: }
344:
345: /**
346: * Return a human-readable string to present this assignment to the user.
347: *
348: * @return the assignment's name as it should be displayed to the user
349: */
350: public FxString getDisplayLabel() {
351: if (label != null && !label.isEmpty()) {
352: return label;
353: } else if (assignedType != null
354: && assignedType.getDescription() != null
355: && !assignedType.getDescription().isEmpty()) {
356: return assignedType.getDescription();
357: } else {
358: return new FxString(alias);
359: }
360: }
361:
362: /**
363: * Get the optional hint
364: *
365: * @return hint
366: */
367: public FxString getHint() {
368: return hint;
369: }
370:
371: /**
372: * Check if an option is set for the requested key
373: *
374: * @param key option key
375: * @return if an option is set for the requested key
376: */
377: public boolean hasOption(String key) {
378: return FxStructureOption.hasOption(key, options);
379: }
380:
381: /**
382: * Get an option entry for the given key, if the key is invalid or not found a <code>FxStructureOption</code> object
383: * will be returned with <code>set</code> set to <code>false</code>, overrideable set to <code>false</code> and value
384: * set to an empty String.
385: *
386: * @param key option key
387: * @return the found option or an object that indicates that the option is not set
388: */
389: public FxStructureOption getOption(String key) {
390: return FxStructureOption.getOption(key, options);
391: }
392:
393: /**
394: * Does this assignment have mappings for the requested script event?
395: *
396: * @param event requested script event
397: * @return if mappings exist
398: */
399: public boolean hasScriptMapping(FxScriptEvent event) {
400: return scriptMapping.get(event) != null
401: && scriptMapping.get(event).length > 0;
402: }
403:
404: /**
405: * Does this assignment have mappings for any script type?
406: *
407: * @return if mappings exist
408: */
409: public boolean hasScriptMappings() {
410: //scriptMapping can be null here (and only here!) during 1st type resolve phase when building the environment
411: return scriptMapping != null && scriptMapping.size() > 0;
412: }
413:
414: /**
415: * Get the mapped script ids for the requested script type
416: *
417: * @param event requested script event
418: * @return mapped script ids or <code>null</code> if mappings do not exist for this assignment
419: */
420: public long[] getScriptMapping(FxScriptEvent event) {
421: long[] ret = scriptMapping.get(event);
422: return ret != null ? ret : EMPTY_SCRIPTMAPPING;
423: }
424:
425: /**
426: * Create an empty FxData entry for this assignment
427: *
428: * @param parent the parent group
429: * @param index the index of the new entry
430: * @return FxData
431: * @throws FxCreateException on errors
432: */
433: public abstract FxData createEmptyData(FxGroupData parent, int index)
434: throws FxCreateException;
435:
436: /**
437: * Create a random FxData entry for this assignment
438: *
439: * @param rnd the Random to use
440: * @param env environment
441: * @param parent the parent group
442: * @param index the index of the new entry
443: * @param maxMultiplicity the maximum multiplicity
444: * @return FxData
445: * @throws FxCreateException on errors
446: */
447: public abstract FxData createRandomData(Random rnd,
448: FxEnvironment env, FxGroupData parent, int index,
449: int maxMultiplicity) throws FxCreateException;
450:
451: /**
452: * Resolve preload dependecies after initial loading
453: *
454: * @param assignments all known assignment
455: */
456: public void resolvePreloadDependencies(
457: List<FxAssignment> assignments) {
458: if (parentGroupAssignment != null
459: && parentGroupAssignment.getAlias() == null) {
460: if (parentGroupAssignment.getId() == FxAssignment.NO_PARENT)
461: parentGroupAssignment = null;
462: else {
463: for (FxAssignment as : assignments)
464: if (as instanceof FxGroupAssignment
465: && as.getId() == parentGroupAssignment
466: .getId()) {
467: parentGroupAssignment = (FxGroupAssignment) as;
468: return;
469: }
470: parentGroupAssignment = null; //if we reach this, the parent assignment could not be found => clear it
471: }
472: }
473: }
474:
475: /**
476: * Resolve references after initial loading
477: *
478: * @param environment environment for references
479: * @throws FxNotFoundException on errors
480: */
481: public void resolveReferences(FxEnvironment environment)
482: throws FxNotFoundException {
483: List<Long> scripts = new ArrayList<Long>(10);
484: for (FxScriptMapping sm : environment.getScriptMappings()) {
485: for (FxScriptMappingEntry sme : sm.getMappedAssignments()) {
486: if (!sme.isActive())
487: continue;
488: if (sme.getId() == this .getId()
489: && !scripts.contains(sm.getScriptId())) {
490: scripts.add(sm.getScriptId());
491: } else if (sme.isDerivedUsage()) {
492: for (long l : sme.getDerivedIds())
493: if (l == this .getId()
494: && !scripts.contains(sm.getScriptId()))
495: scripts.add(sm.getScriptId());
496: }
497: }
498: }
499: if (this .scriptMapping != null)
500: this .scriptMapping.clear();
501: else
502: this .scriptMapping = new HashMap<FxScriptEvent, long[]>(5);
503: for (long scriptId : scripts) {
504: FxScriptEvent event = environment.getScript(scriptId)
505: .getEvent();
506: if (this .scriptMapping.get(event) == null)
507: this .scriptMapping.put(event, new long[] { scriptId });
508: else {
509: long[] types = this .scriptMapping.get(event);
510: long[] ntypes = new long[types.length + 1];
511: System.arraycopy(types, 0, ntypes, 0, types.length);
512: ntypes[ntypes.length - 1] = scriptId;
513: this .scriptMapping.put(event, ntypes);
514: }
515: }
516: }
517:
518: /**
519: * Resolve parent dependecies after initial loading
520: *
521: * @param assignments all known assignments
522: */
523: public void resolveParentDependencies(List<FxAssignment> assignments) {
524: if (!(this instanceof FxGroupAssignment))
525: return;
526: for (FxAssignment a : assignments)
527: if (a.hasParentGroupAssignment()
528: && a.getParentGroupAssignment().getId() == this
529: .getId())
530: ((FxGroupAssignment) this ).addAssignment(a);
531: }
532:
533: /**
534: * Compare function
535: *
536: * @param o other assignment to compare to
537: * @return compare result
538: */
539: public int compareTo(FxAssignment o) {
540: if (this .position < o.position)
541: return -1;
542: if (this .position == o.position)
543: return 0;
544: return 1;
545: }
546:
547: /**
548: * {@inheritDoc}
549: */
550: @Override
551: public boolean equals(Object obj) {
552: return obj instanceof FxAssignment
553: && this .getId() == ((FxAssignment) obj).getId();
554: }
555:
556: /**
557: * {@inheritDoc}
558: */
559: @Override
560: public int hashCode() {
561: return (int) getId();
562: }
563:
564: /**
565: * Sort FxAssignments by their position
566: *
567: * @param assignments FxAssignments to sort
568: * @return sorted List with the assignments
569: */
570: public static List<FxAssignment> sort(List<FxAssignment> assignments) {
571: Collections.sort(assignments);
572: return assignments;
573: }
574:
575: /**
576: * Validate the given value for this assignment.
577: *
578: * @param value the value to be checked
579: * @return true if it is valid for this assignment, false otherwise
580: */
581: public boolean isValid(Object value) {
582: return true;
583: }
584:
585: /**
586: * Is this a system internal assignment?
587: *
588: * @return system internal
589: */
590: public boolean isSystemInternal() {
591: return systemInternal;
592: }
593:
594: /**
595: * Mark this assignment as system internal - this is a one-way INTERNAL function!!!
596: *
597: * @return this
598: */
599: public FxAssignment _setSystemInternal() {
600: this .systemInternal = true;
601: return this ;
602: }
603:
604: /**
605: * {@inheritDoc}
606: */
607: @Override
608: public String toString() {
609: return this.getXPath();
610: }
611: }
|