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.*;
036: import com.flexive.shared.content.FxGroupData;
037: import com.flexive.shared.content.FxPK;
038: import com.flexive.shared.content.FxPermissionUtils;
039: import com.flexive.shared.exceptions.FxCreateException;
040: import com.flexive.shared.exceptions.FxInvalidParameterException;
041: import com.flexive.shared.exceptions.FxNotFoundException;
042: import com.flexive.shared.scripting.FxScriptEvent;
043: import com.flexive.shared.scripting.FxScriptMapping;
044: import com.flexive.shared.scripting.FxScriptMappingEntry;
045: import com.flexive.shared.security.ACL;
046: import com.flexive.shared.security.LifeCycleInfo;
047: import com.flexive.shared.security.UserTicket;
048: import com.flexive.shared.value.FxString;
049: import com.flexive.shared.workflow.Workflow;
050: import org.apache.commons.lang.StringUtils;
051:
052: import java.io.Serializable;
053: import java.util.*;
054:
055: /**
056: * Type definition
057: *
058: * @author Markus Plesser (markus.plesser@flexive.com), UCS - unique computing solutions gmbh (http://www.ucs.at)
059: */
060: public class FxType extends AbstractSelectableObjectWithLabel implements
061: Serializable, SelectableObjectWithLabel {
062: private static final long serialVersionUID = 5598974905776911393L;
063:
064: /**
065: * Virtual ROOT_ID type
066: */
067: public final static long ROOT_ID = 0;
068:
069: /**
070: * Name of the account contact data type
071: */
072: public static final String CONTACTDATA = "CONTACTDATA";
073: /**
074: * Name of the folder data type.
075: */
076: public static final String FOLDER = "FOLDER";
077:
078: protected long id;
079: protected ACL ACL;
080: protected Workflow workflow;
081: protected String name;
082: protected FxString description;
083: protected FxType parent;
084: protected TypeStorageMode storageMode;
085: protected TypeCategory category;
086: protected TypeMode mode;
087: protected boolean checkValidity;
088: protected LanguageMode language;
089: protected TypeState state;
090: protected byte permissions;
091: protected boolean trackHistory;
092: protected long historyAge;
093: protected long maxVersions;
094: protected int maxRelSource;
095: protected int maxRelDestination;
096: protected LifeCycleInfo lifeCycleInfo;
097: protected List<FxType> derivedTypes;
098: protected List<FxTypeRelation> relations;
099: protected List<FxPropertyAssignment> assignedProperties;
100: protected List<FxProperty> uniqueProperties;
101: protected List<FxGroupAssignment> assignedGroups;
102: protected List<FxAssignment> scriptedAssignments;
103: protected Map<FxScriptEvent, long[]> scriptMapping;
104:
105: public FxType(long id, ACL acl, Workflow workflow, String name,
106: FxString description, FxType parent,
107: TypeStorageMode storageMode, TypeCategory category,
108: TypeMode mode, boolean checkValidity,
109: LanguageMode language, TypeState state, byte permissions,
110: boolean trackHistory, long historyAge, long maxVersions,
111: int maxRelSource, int maxRelDestination,
112: LifeCycleInfo lifeCycleInfo, List<FxType> derivedTypes,
113: List<FxTypeRelation> relations) {
114: this .id = id;
115: this .ACL = acl;
116: this .workflow = workflow;
117: this .name = name.toUpperCase();
118: this .description = description;
119: this .parent = parent;
120: this .storageMode = storageMode;
121: this .category = category;
122: this .mode = mode;
123: this .checkValidity = checkValidity;
124: this .language = language;
125: this .state = state;
126: this .permissions = permissions;
127: this .trackHistory = trackHistory;
128: this .historyAge = historyAge;
129: this .maxVersions = maxVersions;
130: this .maxRelSource = maxRelSource;
131: this .maxRelDestination = maxRelDestination;
132: this .lifeCycleInfo = lifeCycleInfo;
133: this .derivedTypes = derivedTypes;
134: this .relations = relations;
135: this .scriptMapping = new HashMap<FxScriptEvent, long[]>(10);
136: }
137:
138: /**
139: * Get the category of this FxType (System, User, ...)
140: *
141: * @return the category.
142: */
143: public TypeCategory getCategory() {
144: return category;
145: }
146:
147: /**
148: * Perform validity checks on instances?
149: *
150: * @return is validity checks are performed on instances
151: */
152: public boolean isCheckValidity() {
153: return checkValidity;
154: }
155:
156: /**
157: * Internal id of this FxType
158: *
159: * @return the internal id of this FxType
160: */
161: public long getId() {
162: return id;
163: }
164:
165: /**
166: * Get the ACL of this type
167: *
168: * @return ACL of this type
169: */
170: public ACL getACL() {
171: return ACL;
172: }
173:
174: /**
175: * Getter for the assigned Workflow
176: *
177: * @return Workflow
178: */
179: public Workflow getWorkflow() {
180: return workflow;
181: }
182:
183: /**
184: * Reload this types workflow, internal method, called from the StructureLoader upon Workflow changes
185: *
186: * @param environment environment with updated workflows
187: */
188: public void reloadWorkflow(FxEnvironment environment) {
189: this .workflow = environment.getWorkflow(this .getWorkflow()
190: .getId());
191: }
192:
193: /**
194: * How are languages handled? (None, Single, Multiple, ...)
195: *
196: * @return how languages are handled
197: */
198: public LanguageMode getLanguage() {
199: return language;
200: }
201:
202: /**
203: * Get the state of this type
204: *
205: * @return TypeState
206: */
207: public TypeState getState() {
208: return state;
209: }
210:
211: /**
212: * Is this FxType defining a content or relation?
213: *
214: * @return mode (Content or Relation)
215: */
216: public TypeMode getMode() {
217: return mode;
218: }
219:
220: /**
221: * Get the name of this FxType
222: *
223: * @return name
224: */
225: public String getName() {
226: return name;
227: }
228:
229: /**
230: * Get the description of this FxType
231: *
232: * @return description
233: */
234: public FxString getDescription() {
235: return description;
236: }
237:
238: /**
239: * {@inheritDoc}
240: */
241: public FxString getLabel() {
242: return description != null && !description.isEmpty() ? description
243: : new FxString(false, name);
244: }
245:
246: /**
247: * Returrn a localized, human-readable name for the type.
248: *
249: * @return a localized, human-readable name for the type.
250: */
251: public String getDisplayName() {
252: if (description != null && !description.isEmpty()) {
253: return description.getBestTranslation();
254: } else {
255: return name;
256: }
257: }
258:
259: /**
260: * Is this type a relation?
261: *
262: * @return if this type is a relation
263: */
264: public boolean isRelation() {
265: return getMode() == TypeMode.Relation;
266: }
267:
268: /**
269: * Is this FxType derived from another?
270: *
271: * @return if this FxType is derived from another
272: * @see FxType#getParent()
273: */
274: public boolean isDerived() {
275: return parent != null;
276: }
277:
278: /**
279: * If this FxType is derived from another FxType get the 'super' FxType
280: *
281: * @return FxType this one is derived from or <code>null</code>
282: */
283: public FxType getParent() {
284: return parent;
285: }
286:
287: /**
288: * Get all FxTypes that are derived from this Type
289: *
290: * @return Iterator of all derived types
291: */
292: public List<FxType> getDerivedTypes() {
293: return derivedTypes;
294: }
295:
296: /**
297: * Get how is data stored internally.
298: *
299: * @return how data is stored internally
300: */
301: public TypeStorageMode getStorageMode() {
302: return storageMode;
303: }
304:
305: /**
306: * Use permissions at all?
307: *
308: * @return if permissions are used at all
309: */
310: public boolean usePermissions() {
311: return permissions != 0;
312: }
313:
314: /**
315: * Use content instance permissions?
316: *
317: * @return if content instance permissions are used
318: */
319: public boolean useInstancePermissions() {
320: return (permissions & FxPermissionUtils.PERM_MASK_INSTANCE) == FxPermissionUtils.PERM_MASK_INSTANCE;
321: }
322:
323: /**
324: * Use property permissions?
325: *
326: * @return if property permissions are used
327: */
328: public boolean usePropertyPermissions() {
329: return (permissions & FxPermissionUtils.PERM_MASK_PROPERTY) == FxPermissionUtils.PERM_MASK_PROPERTY;
330: }
331:
332: /**
333: * Use step permissions?
334: *
335: * @return if step permissions are used
336: */
337: public boolean useStepPermissions() {
338: return (permissions & FxPermissionUtils.PERM_MASK_STEP) == FxPermissionUtils.PERM_MASK_STEP;
339: }
340:
341: /**
342: * Use type permissions?
343: *
344: * @return if type permissions are used
345: */
346: public boolean useTypePermissions() {
347: return (permissions & FxPermissionUtils.PERM_MASK_TYPE) == FxPermissionUtils.PERM_MASK_TYPE;
348: }
349:
350: /**
351: * Track history of changes?
352: *
353: * @return if history of changes is tracked
354: */
355: public boolean isTrackHistory() {
356: return trackHistory;
357: }
358:
359: /**
360: * Get how many days history is tracked (0 = forever)
361: *
362: * @return how many days history is tracked (0 = forever)
363: */
364: public long getHistoryAge() {
365: return historyAge;
366: }
367:
368: /**
369: * Get how many versions of instances are kept (-1 = infinite, 0 = none)
370: *
371: * @return how many versions of instances are kept (-1 = infinite, 0 = none)
372: */
373: public long getMaxVersions() {
374: return maxVersions;
375: }
376:
377: /**
378: * How many source instances may be related to this instance in total? (infinte = <0)
379: *
380: * @return how many source instances may be related to this instance in total? (infinte = <0)
381: */
382: public int getMaxRelSource() {
383: return maxRelSource;
384: }
385:
386: /**
387: * How many destination instances may be related to this instance in total? (infinte = <0)
388: *
389: * @return how many destination instances may be related to this instance in total? (infinte = <0)
390: */
391: public int getMaxRelDestination() {
392: return maxRelDestination;
393: }
394:
395: /**
396: * Get information about changes
397: *
398: * @return information about changes
399: */
400: public LifeCycleInfo getLifeCycleInfo() {
401: return lifeCycleInfo;
402: }
403:
404: /**
405: * Get all group assignments that are attached to the type's root
406: *
407: * @return all group assignments that are attached to the type's root
408: */
409: public List<FxGroupAssignment> getAssignedGroups() {
410: return Collections.unmodifiableList(assignedGroups);
411: }
412:
413: /**
414: * Get all property assignments that are attached to the type's root
415: *
416: * @return all property assignments that are attached to the type's root
417: */
418: public List<FxPropertyAssignment> getAssignedProperties() {
419: return Collections.unmodifiableList(assignedProperties);
420: }
421:
422: /**
423: * Do unique properties for this type exist?
424: *
425: * @return if unique properties for this type exist
426: */
427: public boolean hasUniqueProperties() {
428: return uniqueProperties.size() > 0;
429: }
430:
431: /**
432: * Get all properties used in this type that have a unique constraint set
433: *
434: * @return all properties used in this type that have a unique constraint set
435: */
436: public List<FxProperty> getUniqueProperties() {
437: return Collections.unmodifiableList(uniqueProperties);
438: }
439:
440: /**
441: * Get all possible relation combinations
442: *
443: * @return possible relation combinations
444: */
445: public List<FxTypeRelation> getRelations() {
446: return Collections.unmodifiableList(relations);
447: }
448:
449: /**
450: * Does this type have mappings for the requested script event type?
451: *
452: * @param event requested script event type
453: * @return if mappings exist
454: */
455: public boolean hasScriptMapping(FxScriptEvent event) {
456: return scriptMapping.get(event) != null;
457: }
458:
459: /**
460: * Do scripted assignments exists for this type?
461: *
462: * @return if scripted assignments exist for this type
463: */
464: public boolean hasScriptedAssignments() {
465: return scriptedAssignments != null
466: && scriptedAssignments.size() > 0;
467: }
468:
469: /**
470: * Get a list with all assignments that have scripts assigned for the given script type
471: *
472: * @param event script event
473: * @return list with all assignments that have scripts assigned for the given script type
474: */
475: public synchronized List<FxAssignment> getScriptedAssignments(
476: FxScriptEvent event) {
477: List<FxAssignment> scripts = new ArrayList<FxAssignment>(5);
478: if (!hasScriptedAssignments())
479: return scripts;
480: for (FxAssignment a : scriptedAssignments)
481: if (a.getScriptMapping(event) != null
482: && !scripts.contains(a))
483: scripts.add(a);
484: return scripts;
485: }
486:
487: /**
488: * Get the script id's that are mapped to this type for the requested script type
489: *
490: * @param event requested script event
491: * @return mappings or <code>null</code> if mapping does not exist for this type
492: */
493: public long[] getScriptMapping(FxScriptEvent event) {
494: return scriptMapping.get(event);
495: }
496:
497: /**
498: * Get the permissions set for this type bit coded
499: *
500: * @return bit coded permissions
501: */
502: public byte getBitCodedPermissions() {
503: return permissions;
504: }
505:
506: /**
507: * Resolve references after initial loading
508: *
509: * @param fxStructure structure for references
510: * @throws FxNotFoundException on errors
511: */
512: public void resolveReferences(FxEnvironment fxStructure)
513: throws FxNotFoundException {
514: //we only resolve if the parent is a preload type! => this can and will only happen once
515: if (getParent() != null
516: && getParent().getMode() == TypeMode.Preload) {
517: //assign correct parents
518: if (getParent().getId() == 0)
519: this .parent = null;
520: else {
521: this .parent = fxStructure.getType(getParent().getId());
522: if (!this .parent.derivedTypes.contains(this ))
523: this .parent.derivedTypes.add(this );
524: }
525: //resolve derived types
526: for (FxType derived : fxStructure.getTypes(true, true,
527: true, true)) {
528: if (derived.getParent() != null
529: && derived.getParent().getId() == getId())
530: derivedTypes.add(derived);
531: }
532: if (relations.size() > 0) {
533: for (FxTypeRelation relation : relations)
534: relation.resolveReferences(fxStructure);
535: }
536: }
537: if (assignedProperties == null)
538: assignedProperties = new ArrayList<FxPropertyAssignment>(10);
539: else
540: assignedProperties.clear();
541: if (uniqueProperties == null)
542: uniqueProperties = new ArrayList<FxProperty>(10);
543: else
544: uniqueProperties.clear();
545: if (scriptedAssignments == null)
546: scriptedAssignments = new ArrayList<FxAssignment>(10);
547: else
548: scriptedAssignments.clear();
549: for (FxPropertyAssignment fxpa : fxStructure
550: .getPropertyAssignments(true)) {
551: if (fxpa.getAssignedType().getId() != this .getId())
552: continue;
553: if (fxpa.hasScriptMappings()
554: && !scriptedAssignments.contains(fxpa))
555: scriptedAssignments.add(fxpa);
556:
557: if (!fxpa.hasParentGroupAssignment())
558: assignedProperties.add(fxpa);
559: if (fxpa.getProperty().getUniqueMode() != UniqueMode.None
560: && !uniqueProperties.contains(fxpa.getProperty()))
561: uniqueProperties.add(fxpa.getProperty());
562:
563: }
564: if (assignedGroups == null)
565: assignedGroups = new ArrayList<FxGroupAssignment>(10);
566: else
567: assignedGroups.clear();
568: for (FxGroupAssignment fxga : fxStructure
569: .getGroupAssignments(true)) {
570: if (fxga.getAssignedType().getId() != this .getId())
571: continue;
572: if (fxga.hasScriptMappings()
573: && !scriptedAssignments.contains(fxga))
574: scriptedAssignments.add(fxga);
575: if (!fxga.hasParentGroupAssignment())
576: assignedGroups.add(fxga);
577: }
578:
579: //calculate used script mappings for this type
580: this .scriptMapping.clear();
581: for (FxScriptMapping sm : fxStructure.getScriptMappings()) {
582: for (FxScriptMappingEntry sme : sm.getMappedTypes()) {
583: if (!sme.isActive())
584: continue;
585: if (sme.getId() == this .getId()) {
586: addScriptMapping(this .scriptMapping, sme
587: .getScriptEvent(), sm.getScriptId());
588: } else if (sme.isDerivedUsage()) {
589: for (long l : sme.getDerivedIds())
590: if (l == this .getId())
591: addScriptMapping(this .scriptMapping, sme
592: .getScriptEvent(), sm.getScriptId());
593:
594: }
595: }
596: }
597: }
598:
599: private synchronized void addScriptMapping(
600: Map<FxScriptEvent, long[]> scriptMapping,
601: FxScriptEvent scriptEvent, long scriptId) {
602: if (scriptMapping.get(scriptEvent) == null)
603: scriptMapping.put(scriptEvent, new long[] { scriptId });
604: else {
605: long[] scripts = scriptMapping.get(scriptEvent);
606: if (FxArrayUtils.containsElement(scripts, scriptId))
607: return;
608: long[] new_scripts = new long[scripts.length + 1];
609: System
610: .arraycopy(scripts, 0, new_scripts, 0,
611: scripts.length);
612: new_scripts[new_scripts.length - 1] = scriptId;
613: scriptMapping.put(scriptEvent, new_scripts);
614: }
615: }
616:
617: /**
618: * Create an empty FxData hierarchy for a new FxContent starting with a
619: * virtual root group.
620: *
621: * @param xpPrefix XPath prefix like "FxType name[@pk=..]"
622: * @return empty FxData hierarchy
623: * @throws FxCreateException on errors
624: */
625: public FxGroupData createEmptyData(String xpPrefix)
626: throws FxCreateException {
627: FxGroupData base;
628: try {
629: base = FxGroupData.createVirtualRootGroup(xpPrefix);
630: } catch (FxInvalidParameterException e) {
631: throw new FxCreateException(e);
632: }
633: final UserTicket ticket = FxContext.get().getTicket();
634: for (FxPropertyAssignment fxpa : assignedProperties) {
635: if (!fxpa.isEnabled()
636: || (usePropertyPermissions() && !ticket
637: .mayCreateACL(fxpa.getACL().getId(), ticket
638: .getUserId())))
639: continue;
640: /*if (fxpa.getMultiplicity().isOptional())
641: base.addChild(fxpa.createEmptyData(base, 1));
642: else*/
643: for (int c = 0; c < fxpa.getDefaultMultiplicity(); c++)
644: base.addChild(fxpa.createEmptyData(base, c + 1));
645: }
646:
647: for (FxGroupAssignment fxga : assignedGroups) {
648: if (!fxga.isEnabled())
649: continue;
650: /*if (fxga.getMultiplicity().isOptional())
651: base.addChild(fxga.createEmptyData(base, 1));
652: else*/
653: for (int c = 0; c < fxga.getDefaultMultiplicity(); c++)
654: base.addChild(fxga.createEmptyData(base, c + 1));
655: }
656: return base;
657: }
658:
659: /**
660: * Create a base group with random data
661: *
662: * @param pk primary key of instance that uses this random data
663: * @param env environment
664: * @param rnd Random to use
665: * @param maxMultiplicity the maximum multiplicity for groups
666: * @return random data
667: * @throws FxCreateException on errors
668: */
669: public FxGroupData createRandomData(FxPK pk, FxEnvironment env,
670: Random rnd, int maxMultiplicity) throws FxCreateException {
671: FxGroupData base;
672: try {
673: base = FxGroupData
674: .createVirtualRootGroup(buildXPathPrefix(pk));
675: } catch (FxInvalidParameterException e) {
676: throw new FxCreateException(e);
677: }
678: int count;
679: for (FxPropertyAssignment fxpa : assignedProperties) {
680: if (!fxpa.isEnabled()
681: || fxpa.isSystemInternal()
682: || fxpa.getProperty().getDataType() == FxDataType.Binary
683: || fxpa.getProperty().getDataType() == FxDataType.Reference)
684: continue;
685: count = fxpa.getMultiplicity().getRandomRange(rnd,
686: maxMultiplicity);
687: for (int i = 0; i < count; i++)
688: base.getChildren().add(
689: fxpa.createRandomData(rnd, env, base, i + 1,
690: maxMultiplicity));
691: }
692:
693: for (FxGroupAssignment fxga : assignedGroups) {
694: if (!fxga.isEnabled())
695: continue;
696: count = fxga.getMultiplicity().getRandomRange(rnd,
697: maxMultiplicity);
698: for (int i = 0; i < count; i++)
699: base.getChildren().add(
700: fxga.createRandomData(rnd, env, base, i + 1,
701: maxMultiplicity));
702: }
703: return base;
704: }
705:
706: /**
707: * Get the assignment for the given XPath
708: *
709: * @param parentXPath desired XPath
710: * @return FxAssignment
711: * @throws FxInvalidParameterException if XPath is not valid
712: * @throws FxNotFoundException XPath not found
713: */
714: public FxAssignment getAssignment(String parentXPath)
715: throws FxInvalidParameterException, FxNotFoundException {
716: if (StringUtils.isEmpty(parentXPath) || "/".equals(parentXPath))
717: return null; //connected to the root
718: parentXPath = XPathElement.stripType(parentXPath);
719: List<XPathElement> xpe = XPathElement.split(parentXPath
720: .toUpperCase());
721: if (xpe.size() == 0)
722: return null; //play safe, but should not happen
723: for (FxGroupAssignment rga : getAssignedGroups())
724: if (rga.getAlias().equals(xpe.get(0).getAlias()))
725: return rga.getAssignment(xpe, parentXPath);
726: for (FxPropertyAssignment rpa : getAssignedProperties())
727: if (rpa.getAlias().equals(xpe.get(0).getAlias()))
728: return rpa;
729: throw new FxNotFoundException(
730: "ex.structure.assignment.notFound.xpath", parentXPath);
731: }
732:
733: /**
734: * Get the FxPropertyAssignment for the given XPath.
735: * This is a convenience method calling internally getAssignment and casting the result to FxPropertyAssignment if
736: * appropriate, else throws an FxInvalidParameterException if the assignment is a group.
737: *
738: * @param parentXPath desired XPath
739: * @return FxAssignment
740: * @throws FxInvalidParameterException if XPath is not valid or a group
741: * @throws FxNotFoundException XPath not found
742: */
743: public FxPropertyAssignment getPropertyAssignment(String parentXPath)
744: throws FxInvalidParameterException, FxNotFoundException {
745: FxAssignment pa = getAssignment(parentXPath);
746: if (pa instanceof FxPropertyAssignment)
747: return (FxPropertyAssignment) pa;
748: throw new FxInvalidParameterException("parentXPath",
749: "ex.structure.assignment.noProperty", parentXPath);
750: }
751:
752: /**
753: * Get the FxGroupAssignment for the given XPath.
754: * This is a convenience method calling internally getAssignment and casting the result to FxGroupAssignment if
755: * appropriate, else throws an FxInvalidParameterException if the assignment is a property.
756: *
757: * @param parentXPath desired XPath
758: * @return FxAssignment
759: * @throws FxInvalidParameterException if XPath is not valid or a propery
760: * @throws FxNotFoundException XPath not found
761: */
762: public FxGroupAssignment getGroupAssignment(String parentXPath)
763: throws FxInvalidParameterException, FxNotFoundException {
764: FxAssignment pa = getAssignment(parentXPath);
765: if (pa instanceof FxGroupAssignment)
766: return (FxGroupAssignment) pa;
767: throw new FxInvalidParameterException("parentXPath",
768: "ex.structure.assignment.noGroup", parentXPath);
769: }
770:
771: /**
772: * Get a list of all FxPropertyAssignments connected to this type that are assigned to the requested property
773: *
774: * @param propertyId requested property id
775: * @return list of all FxPropertyAssignments connected to this type that are assigned to the requested property
776: */
777: public List<FxPropertyAssignment> getAssignmentsForProperty(
778: long propertyId) {
779: List<FxPropertyAssignment> ret = new ArrayList<FxPropertyAssignment>(
780: 10);
781: for (FxPropertyAssignment rpa : getAssignedProperties())
782: if (rpa.getProperty().getId() == propertyId)
783: ret.add(rpa);
784: for (FxGroupAssignment rga : getAssignedGroups())
785: for (FxAssignment a : rga.getAllChildAssignments())
786: if (a instanceof FxPropertyAssignment
787: && ((FxPropertyAssignment) a).getProperty()
788: .getId() == propertyId) {
789: ret.add((FxPropertyAssignment) a);
790: }
791: return ret;
792: }
793:
794: /**
795: * Get a list of all FxPropertyAssignments connected to this type that are of the given
796: * {@link FxDataType}.
797: *
798: * @param dataType the data type
799: * @return list of all FxPropertyAssignments connected to this type that are of the given data type
800: */
801: public List<FxPropertyAssignment> getAssignmentsForDataType(
802: FxDataType dataType) {
803: List<FxPropertyAssignment> ret = new ArrayList<FxPropertyAssignment>(
804: 10);
805: for (FxPropertyAssignment rpa : getAssignedProperties())
806: if (rpa.getProperty().getDataType().equals(dataType))
807: ret.add(rpa);
808: for (FxGroupAssignment rga : getAssignedGroups())
809: for (FxAssignment a : rga.getAllChildAssignments())
810: if (a instanceof FxPropertyAssignment
811: && ((FxPropertyAssignment) a).getProperty()
812: .getDataType().equals(dataType)) {
813: ret.add((FxPropertyAssignment) a);
814: }
815: return ret;
816: }
817:
818: /**
819: * Get all assignments directly connected to the given XPath
820: *
821: * @param parentXPath desired XPath
822: * @return ArrayList of FxAssignment
823: * @throws FxInvalidParameterException if XPath is not valid
824: * @throws FxNotFoundException XPath not found
825: */
826: public List<FxAssignment> getConnectedAssignments(String parentXPath)
827: throws FxInvalidParameterException, FxNotFoundException {
828: List<FxAssignment> assignments = new ArrayList<FxAssignment>(10);
829: if (StringUtils.isEmpty(parentXPath) || "/".equals(parentXPath)) {
830: for (FxGroupAssignment rga : getAssignedGroups())
831: if (rga.getParentGroupAssignment() == null)
832: assignments.add(rga);
833: for (FxPropertyAssignment rpa : getAssignedProperties())
834: if (rpa.getParentGroupAssignment() == null)
835: assignments.add(rpa);
836: return Collections.unmodifiableList(FxAssignment
837: .sort(assignments));
838: }
839: //check if the parentXPath is a group
840: if (!(getAssignment(parentXPath) instanceof FxGroupAssignment))
841: throw new FxInvalidParameterException(
842: "ex.structure.assignment.noGroup", parentXPath);
843: for (FxGroupAssignment rga : getAssignedGroups())
844: if (rga.getXPath().equals(parentXPath))
845: return rga.getAssignments();
846: throw new FxNotFoundException(
847: "ex.structure.assignments.notFound.xpath", parentXPath);
848: }
849:
850: /**
851: * Check if the given XPath is valid for this content
852: *
853: * @param XPath the XPath to check
854: * @param checkProperty should the XPath point to a property?
855: * @return if the XPath is valid or not
856: */
857: public boolean isXPathValid(String XPath, boolean checkProperty) {
858: if (StringUtils.isEmpty(XPath))
859: return false;
860: if ("/".equals(XPath))
861: return !checkProperty; //only valid for groups
862: XPath = XPath.toUpperCase();
863: if (!XPath.startsWith("/")) {
864: if (!XPath.startsWith(getName() + "/"))
865: return false;
866: XPath = XPath.substring(XPath.indexOf('/'));
867: }
868: boolean valid = true;
869: try {
870: int depth = 0;
871: FxAssignment last = null;
872: for (XPathElement xpe : XPathElement.split(XPath)) {
873: depth++;
874: if (depth == 1) {
875: //check root assignments
876: boolean found = false;
877: for (FxPropertyAssignment pa : getAssignedProperties()) {
878: if (pa.getAlias().equals(xpe.getAlias())) {
879: last = pa;
880: valid = pa.isEnabled()
881: && pa.getMultiplicity().isValid(
882: xpe.getIndex());
883: found = true;
884: break;
885: }
886: }
887: if (found)
888: continue;
889: for (FxGroupAssignment ga : getAssignedGroups()) {
890: if (ga.getAlias().equals(xpe.getAlias())) {
891: last = ga;
892: valid = ga.isEnabled()
893: && ga.getMultiplicity().isValid(
894: xpe.getIndex());
895: break;
896: }
897: }
898: //end root assignments check
899: } else {
900: if (last == null
901: || (last instanceof FxPropertyAssignment)) {
902: valid = false;
903: break;
904: }
905: for (FxAssignment as : ((FxGroupAssignment) last)
906: .getAssignments()) {
907: if (last != null)
908: last = null; //reset to null for check
909: if (as.getAlias().equals(xpe.getAlias())) {
910: last = as;
911: valid = as.isEnabled()
912: && as.getMultiplicity().isValid(
913: xpe.getIndex());
914: break;
915: }
916: }
917: }
918: if (!valid)
919: break;
920: }
921: if (valid) {
922: if (checkProperty)
923: valid = last instanceof FxPropertyAssignment;
924: else
925: valid = last instanceof FxGroupAssignment;
926: }
927: } catch (FxInvalidParameterException e) {
928: return false;
929: }
930: return valid;
931: }
932:
933: /**
934: * {@inheritDoc}
935: */
936: @Override
937: public String toString() {
938: return this .getName() + "[id=" + this .getId() + "]";
939: }
940:
941: /**
942: * Get this FxType as editable
943: *
944: * @return FxTypeEdit
945: */
946: public FxTypeEdit asEditable() {
947: return new FxTypeEdit(this );
948: }
949:
950: /**
951: * Build an XPath prefix for addressing an instance in XPath's
952: *
953: * @param pk primary key of the instance
954: * @return XPath prefix like "FxType name[@pk=..]"
955: */
956: public String buildXPathPrefix(FxPK pk) {
957: return this .getName().toUpperCase() + "[@pk=" + pk + "]";
958: }
959: }
|