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.CacheAdmin;
036: import com.flexive.shared.FxLanguage;
037: import com.flexive.shared.content.FxPermissionUtils;
038: import com.flexive.shared.exceptions.FxInvalidParameterException;
039: import com.flexive.shared.security.ACL;
040: import com.flexive.shared.value.FxString;
041: import com.flexive.shared.workflow.Workflow;
042: import org.apache.commons.lang.StringUtils;
043:
044: import java.io.Serializable;
045: import java.util.*;
046:
047: /**
048: * FxType used for structure editing
049: *
050: * @author Markus Plesser (markus.plesser@flexive.com), UCS - unique computing solutions gmbh (http://www.ucs.at)
051: */
052: public class FxTypeEdit extends FxType implements Serializable {
053: private static final long serialVersionUID = 3089716188411029437L;
054:
055: private boolean isNew;
056: protected boolean changed;
057: private boolean enableParentAssignments;
058: private boolean removeInstancesWithRelationTypes;
059: private List<FxTypeRelation> originalRelations;
060:
061: /**
062: * Constructor
063: *
064: * @param name name of the type
065: * @param description description
066: * @param acl type ACL
067: * @param workflow workflow to use
068: * @param parent parent type or <code>null</code> if not derived
069: * @param enableParentAssignments if parent is not <code>null</code> enable all derived assignments from the parent?
070: * @param storageMode the storage mode
071: * @param category category mode (system, user)
072: * @param mode type mode (content, relation)
073: * @param checkValidity check validity of contents in search operations
074: * @param language language mode
075: * @param state type state (active, locked, etc)
076: * @param permissions permissions bit coded
077: * @param trackHistory track history
078: * @param historyAge max. age of the history to track
079: * @param maxVersions max. number of versions to keep, if < 0 unlimited
080: * @param maxRelSource max. number of instance related as source
081: * @param maxRelDestination max. number of instances related as destination
082: */
083: private FxTypeEdit(String name, FxString description, ACL acl,
084: Workflow workflow, FxType parent,
085: boolean enableParentAssignments,
086: TypeStorageMode storageMode, TypeCategory category,
087: TypeMode mode, boolean checkValidity,
088: LanguageMode language, TypeState state, byte permissions,
089: boolean trackHistory, long historyAge, long maxVersions,
090: int maxRelSource, int maxRelDestination) {
091: super (-1, acl, workflow, name, description, parent,
092: storageMode, category, mode, checkValidity, language,
093: state, permissions, trackHistory, historyAge,
094: maxVersions, maxRelSource, maxRelDestination, null,
095: null, new ArrayList<FxTypeRelation>(5));
096: this .enableParentAssignments = enableParentAssignments;
097: this .isNew = true;
098: this .changed = true;
099: this .removeInstancesWithRelationTypes = false;
100: this .relations = new ArrayList<FxTypeRelation>(super
101: .getRelations());
102: this .originalRelations = new ArrayList<FxTypeRelation>(super
103: .getRelations());
104: }
105:
106: /**
107: * Create a new FxTypeEdit instance for editing and updating an existing FxType
108: *
109: * @param type the FxType to edit
110: */
111: public FxTypeEdit(FxType type) {
112: super (type.getId(), type.getACL(), type.getWorkflow(), type
113: .getName(), type.getDescription(), type.getParent(),
114: type.getStorageMode(), type.getCategory(), type
115: .getMode(), type.isCheckValidity(), type
116: .getLanguage(), type.getState(),
117: type.permissions, type.isTrackHistory(), type
118: .getHistoryAge(), type.getMaxVersions(), type
119: .getMaxRelSource(),
120: type.getMaxRelDestination(), type.getLifeCycleInfo(),
121: type.getDerivedTypes(), type.getRelations());
122: this .isNew = false;
123: this .scriptMapping = type.scriptMapping;
124: //make lists editable again
125: this .assignedGroups = new ArrayList<FxGroupAssignment>(
126: type.assignedGroups);
127: this .assignedProperties = new ArrayList<FxPropertyAssignment>(
128: type.assignedProperties);
129: this .relations = new ArrayList<FxTypeRelation>(type.relations);
130: this .changed = false;
131: this .removeInstancesWithRelationTypes = false;
132: this .originalRelations = new ArrayList<FxTypeRelation>(super
133: .getRelations());
134: }
135:
136: /**
137: * Create a new FxTypeEdit instance for creating a new FxType
138: *
139: * @param name name of the type
140: * @return FxTypeEdit instance for creating a new FxType
141: */
142: public static FxTypeEdit createNew(String name) {
143: return createNew(
144: name,
145: new FxString(FxLanguage.DEFAULT_ID, name),
146: CacheAdmin
147: .getEnvironment()
148: .getACL(
149: com.flexive.shared.security.ACL.Category.STRUCTURE
150: .getDefaultId()), null);
151: }
152:
153: /**
154: * Create a new FxTypeEdit instance for creating a new FxType
155: *
156: * @param name name of the type
157: * @param description description
158: * @param acl type ACL
159: * @return FxTypeEdit instance for creating a new FxType
160: */
161: public static FxTypeEdit createNew(String name,
162: FxString description, ACL acl) {
163: return createNew(name, description, acl, null);
164: }
165:
166: /**
167: * Create a new FxTypeEdit instance for creating a new FxType
168: *
169: * @param name name of the type
170: * @param description description
171: * @param acl type ACL
172: * @param parent parent type or <code>null</code> if not derived
173: * @return FxTypeEdit instance for creating a new FxType
174: */
175: public static FxTypeEdit createNew(String name,
176: FxString description, ACL acl, FxType parent) {
177: return createNew(name, description, acl, CacheAdmin
178: .getEnvironment().getWorkflows().get(0), parent,
179: parent != null, TypeStorageMode.Hierarchical,
180: TypeCategory.User, TypeMode.Content, false,
181: LanguageMode.Multiple, TypeState.Available,
182: getDefaultTypePermissions(), false, 0, -1, 0, 0);
183: }
184:
185: private static byte getDefaultTypePermissions() {
186: return FxPermissionUtils.encodeTypePermissions(true, false,
187: true, true);
188: }
189:
190: /**
191: * Create a new FxTypeEdit instance for creating a new FxType
192: *
193: * @param name name of the type
194: * @param description description
195: * @param acl type ACL
196: * @param workflow workflow to use
197: * @param parent parent type or <code>null</code> if not derived
198: * @param enableParentAssignments if parent is not <code>null</code> enable all derived assignments from the parent?
199: * @param storageMode the storage mode
200: * @param category category mode (system, user)
201: * @param mode type mode (content, relation)
202: * @param checkValidity check validity of contents in search operations
203: * @param language language mode
204: * @param state type state (active, locked, etc)
205: * @param permissions permissions bit coded
206: * @param trackHistory track history
207: * @param historyAge max. age of the history to track
208: * @param maxVersions max. number of versions to keep, if < 0 unlimited
209: * @param maxRelSource max. number of instance related as source
210: * @param maxRelDestination max. number of instance related as destination
211: * @return FxTypeEdit instance for creating a new FxType
212: */
213: public static FxTypeEdit createNew(String name,
214: FxString description, ACL acl, Workflow workflow,
215: FxType parent, boolean enableParentAssignments,
216: TypeStorageMode storageMode, TypeCategory category,
217: TypeMode mode, boolean checkValidity,
218: LanguageMode language, TypeState state, byte permissions,
219: boolean trackHistory, long historyAge, long maxVersions,
220: int maxRelSource, int maxRelDestination) {
221: return new FxTypeEdit(name, description, acl, workflow, parent,
222: enableParentAssignments, storageMode, category, mode,
223: checkValidity, language, state, permissions,
224: trackHistory, historyAge, maxVersions, maxRelSource,
225: maxRelDestination);
226: }
227:
228: /**
229: * Is this a new type or updating an existing type?
230: *
231: * @return new or existing type?
232: */
233: public boolean isNew() {
234: return isNew;
235: }
236:
237: /**
238: * Returns the unmodifiable script mapping of this type.
239: *
240: * @return the unmodifiable script mapping of this type
241: */
242: /*
243: public Map<FxScriptEvent, long[]> getScriptMapping() {
244: return Collections.unmodifiableMap(scriptMapping);
245: }
246: */
247:
248: /**
249: * If FxTypeRelation entries are removed, remove all affected instances?
250: *
251: * @return remove all affected instances if FxTypeRelation entries are removed?
252: */
253: public boolean isRemoveInstancesWithRelationTypes() {
254: return removeInstancesWithRelationTypes;
255: }
256:
257: /**
258: * Set if affected instances should be removed if FxTypeRelations are removed
259: *
260: * @param removeInstancesWithRelationTypes if affected instances should be removed if FxTypeRelations are removed
261: */
262: public void setRemoveInstancesWithRelationTypes(
263: boolean removeInstancesWithRelationTypes) {
264: this .removeInstancesWithRelationTypes = removeInstancesWithRelationTypes;
265: }
266:
267: /**
268: * Enable parent assignments if creating a derived type?
269: * This method has no effect if editing an existing type!
270: *
271: * @return are parent assignments enabled?
272: */
273: public boolean isEnableParentAssignments() {
274: return enableParentAssignments;
275: }
276:
277: /**
278: * Enable parent assignments if creating a derived type?
279: * This method has no effect if editing an existing type!
280: *
281: * @param enableParentAssignments are parent assignments enabled?
282: * @return the type itself, useful for chained calls
283: */
284: public FxTypeEdit setEnableParentAssignments(
285: boolean enableParentAssignments) {
286: this .enableParentAssignments = enableParentAssignments;
287: this .changed = true;
288: return this ;
289: }
290:
291: /**
292: * Assign a new ACL
293: *
294: * @param ACL ACL to assign for this type
295: * @return the type itself, useful for chained calls
296: */
297: public FxTypeEdit setACL(ACL ACL) {
298: this .ACL = ACL;
299: this .changed = true;
300: return this ;
301: }
302:
303: /**
304: * Set the workflow to use
305: *
306: * @param workflow the workflow to use
307: * @return the type itself, useful for chained calls
308: */
309: public FxTypeEdit setWorkflow(Workflow workflow) {
310: this .workflow = workflow;
311: this .changed = true;
312: return this ;
313: }
314:
315: /**
316: * Set the name of this type
317: *
318: * @param name the name of this type
319: * @return the type itself, useful for chained calls
320: */
321: public FxTypeEdit setName(String name) {
322: if (StringUtils.isEmpty(name))
323: return this ;
324: this .name = name.toUpperCase().trim();
325: this .changed = true;
326: return this ;
327: }
328:
329: /**
330: * Set the types description
331: *
332: * @param description description
333: * @return the type itself, useful for chained calls
334: */
335: public FxTypeEdit setDescription(FxString description) {
336: this .description = description;
337: this .changed = true;
338: return this ;
339: }
340:
341: /*public void setParent(FxType parent) {
342: this.parent = parent;
343: }
344:
345: public void setStorageMode(TypeStorageMode storageMode) {
346: this.storageMode = storageMode;
347: }*/
348:
349: /**
350: * Set the types category (user, system)
351: *
352: * @param category the category to set
353: * @return the type itself, useful for chained calls
354: */
355: public FxTypeEdit setCategory(TypeCategory category) {
356: this .category = category;
357: this .changed = true;
358: return this ;
359: }
360:
361: /**
362: * Set this type's mode.
363: * Setting does not check anything, if the requested mode is really available and possible is determined during saving
364: *
365: * @param mode requested mode
366: * @return this
367: */
368: public FxTypeEdit setMode(TypeMode mode) {
369: this .mode = mode;
370: this .changed = true;
371: return this ;
372: }
373:
374: /**
375: * Check validity (valid from/to) in search operations?
376: *
377: * @param checkValidity check validity
378: * @return the type itself, useful for chained calls
379: */
380: public FxTypeEdit setCheckValidity(boolean checkValidity) {
381: this .checkValidity = checkValidity;
382: this .changed = true;
383: return this ;
384: }
385:
386: /**
387: * Set the language mode to use for this type
388: *
389: * @param language language mode to use
390: * @return the type itself, useful for chained calls
391: */
392: public FxTypeEdit setLanguage(LanguageMode language) {
393: this .language = language;
394: this .changed = true;
395: return this ;
396: }
397:
398: /**
399: * Set the state of this type
400: *
401: * @param state the state of this type
402: * @return the type itself, useful for chained calls
403: */
404: public FxTypeEdit setState(TypeState state) {
405: this .state = state;
406: this .changed = true;
407: return this ;
408: }
409:
410: /**
411: * Set the bit coded permissions of this type
412: *
413: * @param permissions bit coded permissions
414: * @return the type itself, useful for chained calls
415: */
416: public FxTypeEdit setPermissions(byte permissions) {
417: this .permissions = permissions;
418: this .changed = true;
419: return this ;
420: }
421:
422: /**
423: * Set usage of instance permissions
424: *
425: * @param use use instance permissions?
426: * @return the type itself, useful for chained calls
427: */
428: public FxTypeEdit setUseInstancePermissions(boolean use) {
429: if (use == this .useInstancePermissions())
430: return this ;
431: this .permissions ^= FxPermissionUtils.PERM_MASK_INSTANCE;
432: this .changed = true;
433: return this ;
434: }
435:
436: /**
437: * Set usage of property permissions
438: *
439: * @param use use property permissions?
440: * @return the type itself, useful for chained calls
441: */
442: public FxTypeEdit setUsePropertyPermissions(boolean use) {
443: if (use == this .usePropertyPermissions())
444: return this ;
445: this .permissions ^= FxPermissionUtils.PERM_MASK_PROPERTY;
446: this .changed = true;
447: return this ;
448: }
449:
450: /**
451: * Set usage of step permissions
452: *
453: * @param use use step permissions?
454: * @return the type itself, useful for chained calls
455: */
456: public FxTypeEdit setUseStepPermissions(boolean use) {
457: if (use == this .useStepPermissions())
458: return this ;
459: this .permissions ^= FxPermissionUtils.PERM_MASK_STEP;
460: this .changed = true;
461: return this ;
462: }
463:
464: /**
465: * Set usage of type permissions
466: *
467: * @param use use type permissions?
468: * @return the type itself, useful for chained calls
469: */
470: public FxTypeEdit setUseTypePermissions(boolean use) {
471: if (use == this .useTypePermissions())
472: return this ;
473: this .permissions ^= FxPermissionUtils.PERM_MASK_TYPE;
474: this .changed = true;
475: return this ;
476: }
477:
478: /**
479: * Track history for this type
480: *
481: * @param trackHistory track history?
482: * @return the type itself, useful for chained calls
483: */
484: public FxTypeEdit setTrackHistory(boolean trackHistory) {
485: this .trackHistory = trackHistory;
486: this .changed = true;
487: return this ;
488: }
489:
490: /**
491: * Set the max. age of history entries
492: *
493: * @param historyAge max. age of history entries
494: * @return the type itself, useful for chained calls
495: */
496: public FxTypeEdit setHistoryAge(long historyAge) {
497: this .historyAge = (historyAge > 0 ? historyAge : 0);
498: this .changed = true;
499: return this ;
500: }
501:
502: /**
503: * Set the max. number of versions to keep, if negative unlimited
504: *
505: * @param maxVersions max. number of versions to keep, if negative unlimited
506: * @return the type itself, useful for chained calls
507: */
508: public FxTypeEdit setMaxVersions(long maxVersions) {
509: this .maxVersions = (maxVersions < -1 ? -1 : maxVersions);
510: this .changed = true;
511: return this ;
512: }
513:
514: /**
515: * Set the max. number related source instances for this type, if negative unlimited
516: *
517: * @param maxRelSource max. number related source instances for this type, if negative unlimited
518: * @return the type itself, useful for chained calls
519: */
520: public FxTypeEdit setMaxRelSource(int maxRelSource) {
521: this .maxRelSource = maxRelSource;
522: this .changed = true;
523: return this ;
524: }
525:
526: /**
527: * Set the max. number related destination instances for this type, if negative unlimited
528: *
529: * @param maxRelDestination max. number related destination instances for this type, if negative unlimited
530: * @return the type itself, useful for chained calls
531: */
532: public FxTypeEdit setMaxRelDestination(int maxRelDestination) {
533: this .maxRelDestination = maxRelDestination;
534: this .changed = true;
535: return this ;
536: }
537:
538: /**
539: * Find a relation in the given list that matches source and destination type
540: *
541: * @param rel the relation to find
542: * @param list the list to search
543: * @return found relation or <code>null</code>
544: */
545: private FxTypeRelation findRelationInList(FxTypeRelation rel,
546: List<FxTypeRelation> list) {
547: for (FxTypeRelation check : list) {
548: if (check.getSource().getId() == rel.getSource().getId()
549: && check.getDestination().getId() == rel
550: .getDestination().getId())
551: return check;
552: }
553: return null;
554: }
555:
556: /*
557: * Validate the source and destination of a relation.
558: * @param rel the relation to validate.
559: * @throws FxInvalidParameterException if the relation is invalid.
560: */
561:
562: private void validateRelation(FxTypeRelation rel)
563: throws FxInvalidParameterException {
564: if (rel.getSource().isRelation()) {
565: throw new FxInvalidParameterException(
566: "ex.structure.type.relation.wrongTarget", this
567: .getName(), rel.getSource().getName());
568: }
569: if (rel.getDestination().isRelation())
570: throw new FxInvalidParameterException(
571: "ex.structure.type.relation.wrongTarget", this
572: .getName(), rel.getDestination().getName());
573: }
574:
575: /*
576: * Validate if the current relation sources and destination multiplicites together with
577: * newRelationToAdd don't exceed the given maxima (maxRelSource or maxRelDestination).
578: *
579: * @param newRelationToAdd the relation to be added.
580: * @throws FxInvalidParameterException if (maxRelSource or maxRelDestination is exceeded.
581: */
582: private void validateRelationMultiplicity(
583: FxTypeRelation newRelationToAdd)
584: throws FxInvalidParameterException {
585: int source = newRelationToAdd.getMaxSource();
586: int dest = newRelationToAdd.getMaxDestination();
587:
588: if (source == 0 && getMaxRelSource() >= 0)
589: throw new FxInvalidParameterException(
590: "ex.structure.type.relation.maxRelSourceExceeded",
591: this .getName(), newRelationToAdd.getSource()
592: .getName(), newRelationToAdd
593: .getDestination().getName(),
594: getMaxRelSource());
595:
596: else if (dest == 0 && getMaxRelDestination() >= 0)
597: throw new FxInvalidParameterException(
598: "ex.structure.type.relation.maxRelDestExceeded",
599: this .getName(), newRelationToAdd.getSource()
600: .getName(), newRelationToAdd
601: .getDestination().getName(),
602: getMaxRelDestination());
603:
604: for (FxTypeRelation rel : relations) {
605: if (getMaxRelSource() >= 0) {
606: if (rel.getMaxSource() == 0
607: || rel.getMaxSource() + source > getMaxRelSource()) {
608: throw new FxInvalidParameterException(
609: "ex.structure.type.relation.maxRelSourceExceeded",
610: this .getName(), rel.getSource().getName(),
611: rel.getDestination().getName(),
612: getMaxRelSource());
613: } else
614: source += rel.getMaxSource();
615: }
616: if (getMaxRelDestination() >= 0) {
617: if (rel.getMaxDestination() == 0
618: || rel.getMaxDestination() + dest > getMaxRelDestination()) {
619: throw new FxInvalidParameterException(
620: "ex.structure.type.relation.maxRelDestExceeded",
621: this .getName(), rel.getSource().getName(),
622: rel.getDestination().getName(),
623: getMaxRelDestination());
624: } else
625: dest += rel.getMaxDestination();
626: }
627: }
628: }
629:
630: /**
631: * Return if the max source or destination settings for two FxTypeRelations differ
632: *
633: * @param o1 relation 1
634: * @param o2 relation 2
635: * @return differ
636: */
637: private boolean relationDiffers(FxTypeRelation o1, FxTypeRelation o2) {
638: return o1.getMaxDestination() != o2.getMaxDestination()
639: || o1.getMaxSource() != o2.getMaxSource();
640: }
641:
642: /**
643: * Get a list of all FxTypeRelations that have been removed
644: *
645: * @return list of all FxTypeRelations that have been removed
646: */
647: public List<FxTypeRelation> getRemovedRelations() {
648: List<FxTypeRelation> list = new ArrayList<FxTypeRelation>(5);
649: for (FxTypeRelation rel : originalRelations)
650: if (findRelationInList(rel, relations) == null)
651: list.add(rel);
652: return list;
653: }
654:
655: /**
656: * Get a list of all FxTypeRelations that have been added
657: *
658: * @return list of all FxTypeRelations that have been added
659: */
660: public List<FxTypeRelation> getAddedRelations() {
661: List<FxTypeRelation> list = new ArrayList<FxTypeRelation>(5);
662: for (FxTypeRelation rel : relations)
663: if (findRelationInList(rel, originalRelations) == null)
664: list.add(rel);
665: return list;
666: }
667:
668: /**
669: * Get a list of all FxTypeRelations that have been updated
670: *
671: * @return list of all FxTypeRelations that have been updated
672: */
673: public List<FxTypeRelation> getUpdatedRelations() {
674: List<FxTypeRelation> list = new ArrayList<FxTypeRelation>(5);
675: FxTypeRelation curr;
676: for (FxTypeRelation rel : relations) {
677: curr = findRelationInList(rel, originalRelations);
678: if ((curr != null && relationDiffers(curr, rel))
679: || (rel instanceof FxTypeRelationEdit && ((FxTypeRelationEdit) rel)
680: .isChanged()))
681: list.add(rel);
682: }
683: return list;
684: }
685:
686: /**
687: * Add or update a relation
688: *
689: * @param relation the relation to add or update
690: * @return this
691: * @throws FxInvalidParameterException if the relation is invalid.
692: */
693: public FxTypeEdit addRelation(FxTypeRelation relation)
694: throws FxInvalidParameterException {
695: validateRelation(relation);
696: if (relations.contains(relation)) {
697: relations.remove(relation);
698: relations.add(relation);
699: } else
700: relations.add(relation);
701: return this ;
702: }
703:
704: /**
705: * Add or update a relation
706: *
707: * @param relation the relation to add or update
708: * @throws FxInvalidParameterException if the relation is invalid.
709: */
710: public void updateRelation(FxTypeRelation relation)
711: throws FxInvalidParameterException {
712: addRelation(relation);
713: }
714:
715: /**
716: * Remove a relation
717: *
718: * @param relation the relation to remove
719: */
720: public void removeRelation(FxTypeRelation relation) {
721: relations.remove(relation);
722: }
723:
724: /**
725: * {@inheritDoc}
726: */
727: @Override
728: public List<FxGroupAssignment> getAssignedGroups() {
729: return assignedGroups;
730: }
731:
732: /**
733: * {@inheritDoc}
734: */
735: @Override
736: public List<FxPropertyAssignment> getAssignedProperties() {
737: return assignedProperties;
738: }
739:
740: /**
741: * Have changes been made?
742: *
743: * @return changes made
744: */
745: public boolean isChanged() {
746: return changed || getAddedRelations().size() > 0
747: || getRemovedRelations().size() > 0
748: || getUpdatedRelations().size() > 0;
749: }
750: }
|