0001: /***************************************************************
0002: * This file is part of the [fleXive](R) project.
0003: *
0004: * Copyright (c) 1999-2008
0005: * UCS - unique computing solutions gmbh (http://www.ucs.at)
0006: * All rights reserved
0007: *
0008: * The [fleXive](R) project is free software; you can redistribute
0009: * it and/or modify it under the terms of the GNU General Public
0010: * License as published by the Free Software Foundation;
0011: * either version 2 of the License, or (at your option) any
0012: * later version.
0013: *
0014: * The GNU General Public License can be found at
0015: * http://www.gnu.org/copyleft/gpl.html.
0016: * A copy is found in the textfile GPL.txt and important notices to the
0017: * license from the author are found in LICENSE.txt distributed with
0018: * these libraries.
0019: *
0020: * This library is distributed in the hope that it will be useful,
0021: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0022: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
0023: * GNU General Public License for more details.
0024: *
0025: * For further information about UCS - unique computing solutions gmbh,
0026: * please see the company website: http://www.ucs.at
0027: *
0028: * For further information about [fleXive](R), please see the
0029: * project website: http://www.flexive.org
0030: *
0031: *
0032: * This copyright notice MUST APPEAR in all copies of the file!
0033: ***************************************************************/package com.flexive.shared.content;
0034:
0035: import com.flexive.shared.CacheAdmin;
0036: import com.flexive.shared.FxSharedUtils;
0037: import com.flexive.shared.XPathElement;
0038: import com.flexive.shared.exceptions.*;
0039: import com.flexive.shared.interfaces.ContentEngine;
0040: import com.flexive.shared.security.LifeCycleInfo;
0041: import com.flexive.shared.structure.FxEnvironment;
0042: import com.flexive.shared.structure.FxMultiplicity;
0043: import com.flexive.shared.structure.FxPropertyAssignment;
0044: import com.flexive.shared.structure.FxType;
0045: import com.flexive.shared.value.*;
0046: import org.apache.commons.lang.StringUtils;
0047:
0048: import java.io.Serializable;
0049: import java.util.ArrayList;
0050: import java.util.Date;
0051: import java.util.List;
0052: import java.util.Random;
0053:
0054: /**
0055: * A content instance
0056: *
0057: * @author Markus Plesser (markus.plesser@flexive.com), UCS - unique computing solutions gmbh (http://www.ucs.at)
0058: */
0059: public class FxContent implements Serializable, Cloneable {
0060:
0061: private static final long serialVersionUID = -7014370829966212118L;
0062:
0063: /**
0064: * Topmost position for relation positioning
0065: */
0066: public final static int POSITION_TOP = Integer.MIN_VALUE;
0067:
0068: /**
0069: * Bottommost position for relation positioning
0070: */
0071: public final static int POSITION_BOTTOM = Integer.MAX_VALUE;
0072: private FxPK pk;
0073: private long typeId;
0074: private long mandatorId;
0075: private long aclId;
0076: private long stepId;
0077: private int maxVersion;
0078: private int liveVersion;
0079: private long mainLanguage;
0080:
0081: private boolean active;
0082:
0083: private int initialLiveVersion;
0084: private boolean relation;
0085: private FxPK relatedSource;
0086: private FxPK relatedDestination;
0087: private int relatedSourcePosition;
0088:
0089: private int relatedDestinationPosition;
0090:
0091: private LifeCycleInfo lifeCycleInfo;
0092:
0093: private FxGroupData data;
0094: private long binaryPreviewId;
0095: private long binaryPreviewACL;
0096:
0097: /**
0098: * Constructor
0099: *
0100: * @param pk primary key
0101: * @param typeId used type id
0102: * @param relation is this a content for a relation?
0103: * @param mandatorId mandator id
0104: * @param aclId ACL id
0105: * @param stepId step id
0106: * @param maxVersion max. version for this instance
0107: * @param liveVersion live version for this instance (0=no live version exists)
0108: * @param active is this instance active
0109: * @param mainLanguage main language
0110: * @param relatedSource related source instance (only if this is a relation)
0111: * @param relatedDestination related destination instance (only if this is a relation)
0112: * @param relatedSourcePosition position for source instance (only if this is a relation)
0113: * @param relatedDestinationPosition position for destination instance (only if this is a relation)
0114: * @param lifeCycleInfo lifecycle
0115: * @param data data
0116: * @param binaryPreviewId id of the preview binary
0117: * @param binaryPreviewACL id of the ACL of the preview binary
0118: */
0119: public FxContent(FxPK pk, long typeId, boolean relation,
0120: long mandatorId, long aclId, long stepId, int maxVersion,
0121: int liveVersion, boolean active, long mainLanguage,
0122: FxPK relatedSource, FxPK relatedDestination,
0123: int relatedSourcePosition, int relatedDestinationPosition,
0124: LifeCycleInfo lifeCycleInfo, FxGroupData data,
0125: long binaryPreviewId, long binaryPreviewACL) {
0126: this .pk = pk;
0127: this .typeId = typeId;
0128: this .relation = relation;
0129: this .mandatorId = mandatorId;
0130: this .aclId = aclId;
0131: this .stepId = stepId;
0132: this .maxVersion = maxVersion;
0133: this .liveVersion = liveVersion;
0134: this .initialLiveVersion = liveVersion;
0135: this .mainLanguage = mainLanguage;
0136: this .relatedSource = relatedSource;
0137: this .relatedDestination = relatedDestination;
0138: this .relatedSourcePosition = relatedSourcePosition;
0139: this .relatedDestinationPosition = relatedDestinationPosition;
0140: this .lifeCycleInfo = lifeCycleInfo;
0141: this .data = data;
0142: this .active = active;
0143: this .binaryPreviewId = binaryPreviewId;
0144: this .binaryPreviewACL = binaryPreviewACL;
0145: }
0146:
0147: /**
0148: * Getter for the primary key
0149: *
0150: * @return primary key
0151: */
0152: public FxPK getPk() {
0153: return pk;
0154: }
0155:
0156: /**
0157: * Getter for the Id
0158: *
0159: * @return id
0160: */
0161: public long getId() {
0162: return pk.getId();
0163: }
0164:
0165: /**
0166: * Getter for the version
0167: *
0168: * @return version
0169: */
0170: public int getVersion() {
0171: return pk.getVersion();
0172: }
0173:
0174: /**
0175: * Getter for the type id
0176: *
0177: * @return type id
0178: */
0179: public long getTypeId() {
0180: return typeId;
0181: }
0182:
0183: /**
0184: * Getter for the mandator id
0185: *
0186: * @return mandator id
0187: */
0188: public long getMandatorId() {
0189: return mandatorId;
0190: }
0191:
0192: /**
0193: * Getter for the ACL id
0194: *
0195: * @return ACL id
0196: */
0197: public long getAclId() {
0198: return aclId;
0199: }
0200:
0201: /**
0202: * Set the ACL id
0203: *
0204: * @param aclId the ACL id
0205: */
0206: public void setAclId(long aclId) {
0207: this .aclId = aclId;
0208: //TODO: check if ACL is valid!
0209: updateSystemInternalProperties();
0210: }
0211:
0212: /**
0213: * Getter for the step id
0214: *
0215: * @return step id
0216: */
0217: public long getStepId() {
0218: return stepId;
0219: }
0220:
0221: /**
0222: * Set the workflow step id
0223: *
0224: * @param stepId workflow step id
0225: */
0226: public void setStepId(long stepId) {
0227: //TODO: check if step is valid
0228: this .stepId = stepId;
0229: if (CacheAdmin.getEnvironment().getStep(stepId).isLiveStep())
0230: this .liveVersion = this .getPk().getVersion();
0231: else
0232: this .liveVersion = this .initialLiveVersion;
0233: updateSystemInternalProperties();
0234: }
0235:
0236: /**
0237: * Get the max version of this content
0238: *
0239: * @return max version of this content
0240: */
0241: public int getMaxVersion() {
0242: return maxVersion;
0243: }
0244:
0245: /**
0246: * Is this content instance the max version
0247: *
0248: * @return if content instance the max version
0249: */
0250: public boolean isMaxVersion() {
0251: return pk.getVersion() == maxVersion || pk.isNew();
0252: }
0253:
0254: /**
0255: * Get the live version of this content or 0 if no live version exists
0256: *
0257: * @return live version of this content or 0 if no live version exists
0258: */
0259: public int getLiveVersion() {
0260: return liveVersion;
0261: }
0262:
0263: /**
0264: * Is this content instance the live version
0265: *
0266: * @return if content instance the live version
0267: */
0268: public boolean isLiveVersion() {
0269: return pk.getVersion() == liveVersion
0270: || (pk.isNew() && liveVersion == 1);
0271: }
0272:
0273: /**
0274: * Checks if the given PK matches this content. This allows to match a generic PK (i.e. without
0275: * a distinct version, but {@link FxPK#LIVE} or {@link FxPK#MAX}), which the FxPK equals method
0276: * cannot do.
0277: *
0278: * @param otherPk the PK to be matched
0279: * @return true if otherPk matches this content
0280: */
0281: public boolean matchesPk(FxPK otherPk) {
0282: return pk.equals(otherPk)
0283: || (pk.getId() == otherPk.getId() && ((otherPk
0284: .getVersion() == FxPK.MAX && isMaxVersion()) || (otherPk
0285: .getVersion() == FxPK.LIVE && isLiveVersion())));
0286: }
0287:
0288: /**
0289: * Get the main language
0290: *
0291: * @return main language
0292: */
0293: public long getMainLanguage() {
0294: return mainLanguage;
0295: }
0296:
0297: /**
0298: * Set the main language
0299: *
0300: * @param mainLanguage main language
0301: */
0302: public void setMainLanguage(long mainLanguage) {
0303: this .mainLanguage = mainLanguage;
0304: updateSystemInternalProperties();
0305: }
0306:
0307: /**
0308: * Is this content active?
0309: *
0310: * @return content is active
0311: */
0312: public boolean isActive() {
0313: return active;
0314: }
0315:
0316: /**
0317: * (De-)activate this content
0318: *
0319: * @param active active flag
0320: */
0321: public void setActive(boolean active) {
0322: this .active = active;
0323: updateSystemInternalProperties();
0324: }
0325:
0326: /**
0327: * Is this content a relation?
0328: *
0329: * @return content is relation
0330: */
0331: public boolean isRelation() {
0332: return relation;
0333: }
0334:
0335: /**
0336: * If this is a relation get the assigned "from" (or source) instance
0337: *
0338: * @return the assigned "from" (or source) instance
0339: */
0340: public FxPK getRelatedSource() {
0341: return relatedSource;
0342: }
0343:
0344: /**
0345: * Set the primary key of the source relation
0346: *
0347: * @param src source relation
0348: * @return this
0349: */
0350: public FxContent setRelatedSource(FxPK src) {
0351: this .relatedSource = src;
0352: return this ;
0353: }
0354:
0355: /**
0356: * If this is a relation get the assigned "to" (or destination) instance
0357: *
0358: * @return the assigned "to" (or destination) instance
0359: */
0360: public FxPK getRelatedDestination() {
0361: return relatedDestination;
0362: }
0363:
0364: /**
0365: * Set the primary key of the destination relation
0366: *
0367: * @param dst destination relation
0368: * @return this
0369: */
0370: public FxContent setRelatedDestination(FxPK dst) {
0371: this .relatedDestination = dst;
0372: return this ;
0373: }
0374:
0375: /**
0376: * Get the position for the source content instance
0377: *
0378: * @return position for the source content instance
0379: */
0380: public int getRelatedSourcePosition() {
0381: return relatedSourcePosition;
0382: }
0383:
0384: /**
0385: * Get the position for the destination content instance
0386: *
0387: * @return position for the destination content instance
0388: */
0389: public int getRelatedDestinationPosition() {
0390: return relatedDestinationPosition;
0391: }
0392:
0393: public void setRelatedDestinationPosition(
0394: int relatedDestinationPosition) {
0395: this .relatedDestinationPosition = relatedDestinationPosition;
0396: }
0397:
0398: /**
0399: * Get the lifecycle information
0400: *
0401: * @return lifecycle information
0402: */
0403: public LifeCycleInfo getLifeCycleInfo() {
0404: return lifeCycleInfo;
0405: }
0406:
0407: /**
0408: * Get all FxData (Group or Property) entries for the given XPath
0409: *
0410: * @param XPath requested XPath
0411: * @return FxData elements for the given XPath
0412: * @throws FxInvalidParameterException for invalid XPath provided
0413: * @throws FxNotFoundException if no match was found
0414: */
0415: public List<FxData> getData(String XPath)
0416: throws FxInvalidParameterException, FxNotFoundException {
0417: List<FxData> base = data.getChildren();
0418: if (StringUtils.isEmpty(XPath) || "/".equals(XPath))
0419: return base;
0420: List<FxData> ret = base;
0421: boolean found;
0422: for (XPathElement xpe : XPathElement.split(XPath.toUpperCase())) {
0423: found = false;
0424: for (FxData curr : ret) {
0425: if (curr.getXPathElement().equals(xpe)) {
0426: if (curr.isProperty()) {
0427: ret = new ArrayList<FxData>(1);
0428: ret.add(curr);
0429: return ret;
0430: } else {
0431: ret = ((FxGroupData) curr).getChildren();
0432: found = true;
0433: break;
0434: }
0435: }
0436: }
0437: if (!found)
0438: throw new FxNotFoundException(
0439: "ex.content.xpath.notFound", XPath);
0440: }
0441: return ret;
0442: }
0443:
0444: /**
0445: * Get the FxPropertyData entry for the given XPath
0446: *
0447: * @param XPath requested XPath
0448: * @return FxPropertyData entry for the given XPath
0449: * @throws FxInvalidParameterException for invalid XPath provided or XPath is no property
0450: * @throws FxNotFoundException if no match was found
0451: */
0452: public FxPropertyData getPropertyData(String XPath)
0453: throws FxNotFoundException, FxInvalidParameterException {
0454: FxSharedUtils.checkParameterEmpty(XPath, "XPATH");
0455: XPath = XPathElement.stripType(XPath);
0456: List<FxData> found = getData(XPath);
0457: if (found.size() != 1
0458: || !(found.get(0) instanceof FxPropertyData))
0459: throw new FxInvalidParameterException("XPATH",
0460: "ex.xpath.element.noProperty", XPath);
0461: return (FxPropertyData) found.get(0);
0462: }
0463:
0464: /**
0465: * Get a list of all FxPropertyData entries that are assigned to propertyId
0466: *
0467: * @param propertyId the property id requested
0468: * @param includeEmpty include empty data instances?
0469: * @return list of all FxPropertyData entries that are assigned to propertyId
0470: */
0471: public List<FxPropertyData> getPropertyData(long propertyId,
0472: boolean includeEmpty) {
0473: return getRootGroup().getPropertyData(propertyId, includeEmpty);
0474: }
0475:
0476: /**
0477: * Get the FxGroupData entry for the given XPath
0478: *
0479: * @param XPath requested XPath
0480: * @return FxGroupData entry for the given XPath
0481: * @throws FxInvalidParameterException for invalid XPath provided or XPath is no property
0482: * @throws FxNotFoundException if no match was found
0483: */
0484: public FxGroupData getGroupData(String XPath)
0485: throws FxNotFoundException, FxInvalidParameterException {
0486: FxSharedUtils.checkParameterEmpty(XPath, "XPATH");
0487: XPath = XPathElement.stripType(XPathElement.toXPathMult(XPath
0488: .toUpperCase()));
0489: //this is a slightly modified version of getData() but since groups may not contain children its safer
0490: List<FxData> base = data.getChildren();
0491: if (StringUtils.isEmpty(XPath) || "/".equals(XPath))
0492: return getRootGroup();
0493: List<FxData> currChildren = base;
0494: FxGroupData group = null;
0495: boolean found;
0496: for (XPathElement xpe : XPathElement.split(XPath.toUpperCase())) {
0497: found = false;
0498: for (FxData curr : currChildren) {
0499: if (curr.getXPathElement().equals(xpe)) {
0500: if (curr.isProperty()) {
0501: throw new FxInvalidParameterException("XPATH",
0502: "ex.xpath.element.noGroup", XPath);
0503: } else {
0504: currChildren = ((FxGroupData) curr)
0505: .getChildren();
0506: group = ((FxGroupData) curr);
0507: found = true;
0508: break;
0509: }
0510: }
0511: }
0512: if (!found)
0513: throw new FxNotFoundException(
0514: "ex.content.xpath.notFound", XPath);
0515: }
0516: return group;
0517: }
0518:
0519: /**
0520: * Get the (virtual) root group of this content
0521: *
0522: * @return root group
0523: */
0524: public FxGroupData getRootGroup() {
0525: return this .data;
0526: }
0527:
0528: /**
0529: * Set a properties value, needed groups will be created
0530: *
0531: * @param XPath FQ XPath
0532: * @param value value to apply
0533: * @throws FxNotFoundException if the requested XPath does not exist
0534: * @throws FxInvalidParameterException if the request XPath is invalid
0535: * @throws FxNoAccessException if the property for this XPath is marked readonly or no access
0536: * @throws FxCreateException if missing XPath entries failed to be created
0537: */
0538: public void setValue(String XPath, FxValue value)
0539: throws FxNotFoundException, FxInvalidParameterException,
0540: FxNoAccessException, FxCreateException {
0541: XPath = XPathElement.stripType(XPath);
0542: createXPath(XPath);
0543: List<FxData> prop = getData(XPath);
0544: if (prop.size() != 1)
0545: throw new FxInvalidParameterException("XPATH",
0546: "ex.xpath.element.ambiguous.property", XPath);
0547: if (!(prop.get(0) instanceof FxPropertyData))
0548: throw new FxInvalidParameterException("XPATH",
0549: "ex.xpath.element.noProperty", XPath);
0550: ((FxPropertyData) prop.get(0)).setValue(value);
0551: }
0552:
0553: /**
0554: * Create (if possible and not already exists) the given XPath
0555: *
0556: * @param XPath the XPath to create
0557: * @throws FxInvalidParameterException wrong XPath
0558: * @throws FxNotFoundException wrong XPath
0559: * @throws FxCreateException on errors creating
0560: * @throws FxNoAccessException if req. XPath is not accessible
0561: */
0562: private void createXPath(String XPath)
0563: throws FxInvalidParameterException, FxNotFoundException,
0564: FxCreateException, FxNoAccessException {
0565: FxEnvironment env = CacheAdmin.getEnvironment();
0566: if (!env.getType(this .getTypeId()).isXPathValid(XPath, true))
0567: throw new FxInvalidParameterException("XPATH",
0568: "ex.content.xpath.set.invalid", XPath, env.getType(
0569: getTypeId()).getName());
0570: XPath = XPath.toUpperCase();
0571: List<XPathElement> elements = XPathElement.split(XPath);
0572: FxGroupData currGroup = this .getRootGroup();
0573: boolean found;
0574: List<XPathElement> missing = new ArrayList<XPathElement>(20);
0575: for (int i = 0; i < elements.size(); i++) {
0576: found = false;
0577: missing.clear();
0578: for (int m = 1; m < elements.get(i).getIndex(); m++)
0579: missing.add(new XPathElement(
0580: elements.get(i).getAlias(), m, true));
0581: for (FxData currData : currGroup.getChildren()) {
0582: if (currData.getXPathElement().equals(elements.get(i))) {
0583: if (currData instanceof FxPropertyData)
0584: return; //last element reached and it exists
0585: found = true;
0586: currGroup = (FxGroupData) currData;
0587: break;
0588: } else if (missing.contains(currData.getXPathElement())) {
0589: missing.remove(currData.getXPathElement());
0590: }
0591: }
0592: if (found)
0593: continue;
0594: if (missing.size() > 0) {
0595: List<XPathElement> missingPath = new ArrayList<XPathElement>(
0596: i + 1);
0597: missingPath.addAll(elements.subList(0, i));
0598: for (XPathElement currMissing : missing) {
0599: missingPath.add(currMissing);
0600: // System.out.println("Creating missing: "+XPathElement.toXPath(missingPath));
0601: currGroup.addEmptyChild(XPathElement
0602: .toXPath(missingPath),
0603: FxData.POSITION_BOTTOM);
0604: missingPath.remove(missingPath.size() - 1);
0605: }
0606: }
0607: //create the group or property
0608: // System.out.println("Creating: "+XPathElement.toXPath(elements.subList(0, i+1)));
0609: FxData added = currGroup.addEmptyChild(XPathElement
0610: .toXPath(elements.subList(0, i + 1)),
0611: FxData.POSITION_BOTTOM);
0612: if (added instanceof FxGroupData)
0613: currGroup = (FxGroupData) added;
0614: }
0615: }
0616:
0617: /**
0618: * Get the value of a (property) XPath.
0619: * This is actually a convenience method that internally calls <code>getPropertyData(XPath).getValuXDEPTHe()</code>
0620: *
0621: * @param XPath requested XPath
0622: * @return FxValue
0623: * @throws FxNotFoundException on errors
0624: * @throws FxInvalidParameterException on errors
0625: * @see #getPropertyData(String)
0626: */
0627: public FxValue getValue(String XPath) throws FxNotFoundException,
0628: FxInvalidParameterException {
0629: return getPropertyData(XPath).getValue();
0630: }
0631:
0632: /**
0633: * Check if a value exists for the given XPath that is not empty
0634: *
0635: * @param XPath the XPath to check
0636: * @return if a value exists for the given XPath that is not empty
0637: */
0638: public boolean containsValue(String XPath) {
0639: try {
0640: return !getValue(XPath).isEmpty();
0641: } catch (Exception e) {
0642: return false;
0643: }
0644: }
0645:
0646: /**
0647: * Check if the given XPath is valid for this content.
0648: * This is a shortcut to the corresponding type's method!
0649: *
0650: * @param XPath the XPath to check
0651: * @param checkProperty should the XPath point to a property?
0652: * @return if the XPath is valid or not
0653: * @see FxType#isXPathValid(String,boolean)
0654: */
0655: public boolean isXPathValid(String XPath, boolean checkProperty) {
0656: return CacheAdmin.getEnvironment().getType(this .getTypeId())
0657: .isXPathValid(XPath, checkProperty);
0658: }
0659:
0660: /**
0661: * Drop all data and create random entries for testing purposes
0662: *
0663: * @param maxMultiplicity the maximum multiplicity for groups
0664: * @return this
0665: * @throws FxCreateException on errors
0666: * @throws FxNotFoundException on errors
0667: * @throws FxInvalidParameterException on errors
0668: */
0669: public FxContent randomize(int maxMultiplicity)
0670: throws FxCreateException, FxNotFoundException,
0671: FxInvalidParameterException {
0672: Random r = new Random();
0673: FxEnvironment env = CacheAdmin.getEnvironment();
0674: this .data = env.getType(this .getTypeId()).createRandomData(pk,
0675: env, r, maxMultiplicity);
0676: initSystemProperties();
0677: return this ;
0678: }
0679:
0680: public FxContent randomize() throws FxCreateException,
0681: FxNotFoundException, FxInvalidParameterException {
0682: return randomize(FxMultiplicity.RANDOM_MAX);
0683: }
0684:
0685: /**
0686: * Move data (group or property) within its hierarchy for <code>delta</code>
0687: * positions up or down depending on the sign of <code>delta</code> without wrapping
0688: * around if top or bottom position is reached.
0689: * If delta is Integer.MAX_VALUE the data will always be placed at the bottom,
0690: * Integer.MIN_VALUE will always place it at the top.
0691: *
0692: * @param XPath FQ XPath
0693: * @param delta relative number of positions to move
0694: * @throws FxInvalidParameterException for invalid XPath
0695: * @throws FxNotFoundException XPath does not exist for this content
0696: */
0697: public void move(String XPath, int delta)
0698: throws FxInvalidParameterException, FxNotFoundException {
0699: if (delta == 0 || StringUtils.isEmpty(XPath)
0700: || "/".equals(XPath))
0701: return; //nothing to do
0702: List<FxData> mdata = getData(XPath);
0703: FxGroupData parent = mdata.get(0).getParent();
0704: XPathElement last = XPathElement.lastElement(XPath);
0705: parent.moveChild(last, delta);
0706: }
0707:
0708: /**
0709: * Remove the property or group denoted by XPath
0710: *
0711: * @param XPath the XPath to remove
0712: * @throws FxInvalidParameterException if the requested XPath is required and can not be removed
0713: * @throws FxNotFoundException if XPath is incorrect
0714: * @throws FxNoAccessException if data that is to be removed is readonly or no access
0715: */
0716: public void remove(String XPath)
0717: throws FxInvalidParameterException, FxNotFoundException,
0718: FxNoAccessException {
0719: FxSharedUtils.checkParameterEmpty(XPath, "XPATH");
0720: XPath = XPathElement.stripType(XPathElement.toXPathMult(XPath));
0721: List<FxData> found = getData(XPath);
0722: FxData data = null;
0723: if (found.size() == 1) {
0724: if (found.get(0).getXPathFull().equals(XPath))
0725: data = found.get(0); //property
0726: }
0727: if (data == null
0728: && found.get(0).getParent() != null
0729: && found.get(0).getParent().getXPathFull()
0730: .equals(XPath))
0731: data = found.get(0).getParent(); //group with single or multiple properties->get parent
0732: if (data == null || data.getParent() == null)
0733: throw new FxNoAccessException(
0734: "ex.content.xpath.remove.invalid", XPath);
0735:
0736: data.getParent().removeChild(data);
0737: }
0738:
0739: /**
0740: * Check if all required properties are present and valid, etc.
0741: *
0742: * @throws FxInvalidParameterException if required properties are not present or the content is not valid
0743: */
0744: public void checkValidity() throws FxInvalidParameterException {
0745: _checkGroupValidity(data);
0746: }
0747:
0748: /**
0749: * Recursively check a group and its properties and subgroups if required properties are present
0750: *
0751: * @param data FxGroupData to check
0752: * @throws FxInvalidParameterException if required properties are not present
0753: */
0754: private void _checkGroupValidity(FxGroupData data)
0755: throws FxInvalidParameterException {
0756: if (data.getAssignmentMultiplicity().isOptional()
0757: && data.isEmpty())
0758: return; //if optional groups have required properties or subgroups it still is ok if they are empty!
0759: for (FxData curr : data.getChildren()) {
0760: if (curr instanceof FxPropertyData) {
0761: ((FxPropertyData) curr).checkRequired();
0762: } else
0763: _checkGroupValidity((FxGroupData) curr);
0764: }
0765: }
0766:
0767: public FxContent initSystemProperties() throws FxNotFoundException,
0768: FxInvalidParameterException {
0769: FxEnvironment env = CacheAdmin.getEnvironment();
0770: FxType type = env.getType(this .getTypeId());
0771: FxValue value;
0772: for (FxPropertyAssignment sp : env
0773: .getSystemInternalRootPropertyAssignments()) {
0774: if (sp.getAlias().equals("ID"))
0775: value = new FxLargeNumber(false, this .getId());
0776: else if (sp.getAlias().equals("VERSION"))
0777: value = new FxNumber(false, this .getVersion());
0778: else if (sp.getAlias().equals("TYPEDEF"))
0779: value = new FxLargeNumber(false, this .getTypeId());
0780: else if (sp.getAlias().equals("MANDATOR"))
0781: value = new FxLargeNumber(false, this .getMandatorId());
0782: else if (sp.getAlias().equals("ACL"))
0783: value = new FxLargeNumber(false, this .getAclId());
0784: else if (sp.getAlias().equals("STEP"))
0785: value = new FxLargeNumber(false, this .getStepId());
0786: else if (sp.getAlias().equals("MAX_VER"))
0787: value = new FxNumber(false, this .getMaxVersion());
0788: else if (sp.getAlias().equals("LIVE_VER"))
0789: value = new FxNumber(false, this .getLiveVersion());
0790: else if (sp.getAlias().equals("ISMAX_VER"))
0791: value = new FxBoolean(false, this .isMaxVersion());
0792: else if (sp.getAlias().equals("ISLIVE_VER"))
0793: value = new FxBoolean(false, this .isLiveVersion());
0794: else if (sp.getAlias().equals("ISACTIVE"))
0795: value = new FxBoolean(false, this .isActive());
0796: else if (sp.getAlias().equals("MAINLANG"))
0797: value = new FxLargeNumber(false, this .getMainLanguage());
0798: else if (sp.getAlias().equals("CREATED_BY"))
0799: value = new FxLargeNumber(false, this
0800: .getLifeCycleInfo().getCreatorId());
0801: else if (sp.getAlias().equals("CREATED_AT"))
0802: value = new FxDateTime(false, new Date(this
0803: .getLifeCycleInfo().getCreationTime()));
0804: else if (sp.getAlias().equals("MODIFIED_BY"))
0805: value = new FxLargeNumber(false, this
0806: .getLifeCycleInfo().getModificatorId());
0807: else if (sp.getAlias().equals("MODIFIED_AT"))
0808: value = new FxDateTime(false, new Date(this
0809: .getLifeCycleInfo().getModificationTime()));
0810: else if (type.isRelation()) {
0811: if (sp.getAlias().equals("RELSRC"))
0812: value = new FxReference(false,
0813: new ReferencedContent(this
0814: .getRelatedSource()));
0815: else if (sp.getAlias().equals("RELDST"))
0816: value = new FxReference(false,
0817: new ReferencedContent(this
0818: .getRelatedDestination()));
0819: else if (sp.getAlias().equals("RELSRC_POS"))
0820: value = new FxNumber(false, this
0821: .getRelatedSourcePosition());
0822: else if (sp.getAlias().equals("RELDST_POS"))
0823: value = new FxNumber(false, this
0824: .getRelatedDestinationPosition());
0825: else
0826: value = null;
0827: } else
0828: value = null;
0829: if (value != null) {
0830: FxPropertyAssignment this pa = (FxPropertyAssignment) env
0831: .getAssignment(type.getName() + "/"
0832: + sp.getAlias());
0833: this .data.addProperty(XPathElement.toXPathMult("/"
0834: + this pa.getAlias()), this pa, value, this pa
0835: .getPosition());
0836: }
0837: }
0838: return this ;
0839: }
0840:
0841: /**
0842: * Update all system internal properties that provide setters to reflect changes in the FxPropertyData's
0843: */
0844: private void updateSystemInternalProperties() {
0845: try {
0846: FxLargeNumber _long = (FxLargeNumber) getValue("/STEP");
0847: _long.setValue(stepId);
0848: _long = (FxLargeNumber) getValue("/ACL");
0849: _long.setValue(aclId);
0850:
0851: FxLargeNumber _langlong = (FxLargeNumber) getValue("/MAINLANG");
0852: _langlong.setValue(mainLanguage);
0853:
0854: FxBoolean _bool = (FxBoolean) getValue("/ISACTIVE");
0855: _bool.setValue(isActive());
0856:
0857: } catch (Exception e) {
0858: //bad luck
0859: }
0860: }
0861:
0862: /**
0863: * Get a list of all property XPaths contained in this content in correct order
0864: *
0865: * @return list of all property XPaths contained in this content in correct order
0866: */
0867: public List<String> getAllPropertyXPaths() {
0868: List<String> xpaths = new ArrayList<String>(30);
0869: _addXPaths(xpaths, this .getRootGroup(), false, null);
0870: return xpaths;
0871: }
0872:
0873: /**
0874: * Get a list of all XPaths contained in this content in correct order
0875: *
0876: * @param groupPostfix String to append to found groups (useful to append "/" to kind of mark those XPaths as group XPaths)
0877: * @return list of all XPaths contained in this content in correct order
0878: */
0879: public List<String> getAllXPaths(String groupPostfix) {
0880: List<String> xpaths = new ArrayList<String>(30);
0881: _addXPaths(xpaths, this .getRootGroup(), true, groupPostfix);
0882: return xpaths;
0883: }
0884:
0885: /**
0886: * Recursively add all xpaths
0887: *
0888: * @param xpaths list of xpaths to build
0889: * @param group the current group to process
0890: * @param includeGroups include groups?
0891: * @param groupPostfix String to append to found groups (useful to append "/" to kind of mark those XPaths as group XPaths)
0892: */
0893: private void _addXPaths(List<String> xpaths, FxGroupData group,
0894: boolean includeGroups, String groupPostfix) {
0895: for (FxData child : group.getChildren()) {
0896: if (child instanceof FxGroupData) {
0897: if (includeGroups)
0898: xpaths.add(child.getXPathFull() + groupPostfix);
0899: _addXPaths(xpaths, (FxGroupData) child, includeGroups,
0900: groupPostfix);
0901: } else if (child instanceof FxPropertyData)
0902: xpaths.add(child.getXPathFull());
0903: }
0904: }
0905:
0906: /**
0907: * Is a preview available that is not a default image?
0908: *
0909: * @return preview available
0910: */
0911: public boolean isPreviewAvailable() {
0912: return binaryPreviewId >= 0;
0913: }
0914:
0915: /**
0916: * Id of the binary used for previews
0917: *
0918: * @return id of the binary used for previews
0919: */
0920: public long getBinaryPreviewId() {
0921: return binaryPreviewId;
0922: }
0923:
0924: /**
0925: * ACL that is needed to view the preview image
0926: *
0927: * @return ACL that is needed to view the preview image
0928: */
0929: public long getBinaryPreviewACL() {
0930: return binaryPreviewACL;
0931: }
0932:
0933: /**
0934: * Set the binary preview to an XPath.
0935: * Illegal or non-existing values will be ignored!
0936: *
0937: * @param XPath the XPath of the requested binary to set as preview
0938: */
0939: public void setBinaryPreview(String XPath) {
0940: try {
0941: FxPropertyData data = this .getPropertyData(XPath);
0942: if (!(data.getValue() instanceof FxBinary))
0943: return;
0944: binaryPreviewId = ((FxBinary) data.getValue())
0945: .getDefaultTranslation().getId();
0946: binaryPreviewACL = ((FxPropertyAssignment) data
0947: .getAssignment()).getACL().getId();
0948: } catch (Exception e) {
0949: //ignore
0950: }
0951: }
0952:
0953: /**
0954: * Set the binary preview.
0955: * Illegal or non-existing values will be ignored!
0956: *
0957: * @param binaryId if of the requested binary to set as preview
0958: */
0959: public void setBinaryPreview(long binaryId) {
0960: FxPropertyData data = checkPreviewIdExists(binaryId, this
0961: .getRootGroup().getChildren());
0962: if (data == null)
0963: return;
0964: binaryPreviewId = ((FxBinary) data.getValue())
0965: .getDefaultTranslation().getId();
0966: binaryPreviewACL = ((FxPropertyAssignment) data.getAssignment())
0967: .getACL().getId();
0968: }
0969:
0970: /**
0971: * Internal method that tries to find a matching preview image.
0972: * Searches for images and then regular binaries (preview is then set matching the mime type display).
0973: * If neither are found the BinaryDescriptor.SYS_UNKNOWN image is used
0974: *
0975: * @see com.flexive.shared.value.BinaryDescriptor#SYS_UNKNOWN
0976: */
0977: public void resolveBinaryPreview() {
0978: if (binaryPreviewId >= 0) {
0979: //check if the image (still) exists
0980: if (checkPreviewIdExists(binaryPreviewId, this
0981: .getRootGroup().getChildren()) == null) {
0982: resetBinaryPreview();
0983: resolveBinaryPreview();
0984: }
0985: return;
0986: }
0987: FxPropertyData bin = resolveFirstImageData(this .getRootGroup()
0988: .getChildren());
0989: if (bin == null)
0990: bin = resolveFirstBinaryData(this .getRootGroup()
0991: .getChildren());
0992: if (bin == null)
0993: resetBinaryPreview();
0994: else {
0995: binaryPreviewId = ((FxBinary) bin.getValue())
0996: .getDefaultTranslation().getId();
0997: binaryPreviewACL = ((FxPropertyAssignment) bin
0998: .getAssignment()).getACL().getId();
0999: }
1000: }
1001:
1002: /**
1003: * Check if an image with the given id exists in this FxContent instance
1004: *
1005: * @param binaryPreviewId the binary preview id to search
1006: * @param groupData the group data entries to inspect
1007: * @return <code>true</code> if it exists
1008: */
1009: private FxPropertyData checkPreviewIdExists(long binaryPreviewId,
1010: List<FxData> groupData) {
1011: FxPropertyData ret;
1012: for (FxData data : groupData) {
1013: if (data.isGroup()) {
1014: ret = checkPreviewIdExists(binaryPreviewId,
1015: ((FxGroupData) data).getChildren());
1016: if (ret != null)
1017: return ret;
1018: }
1019: if (data.isProperty()
1020: && data instanceof FxPropertyData
1021: && ((FxPropertyData) data).getValue() instanceof FxBinary) {
1022: FxBinary bin = (FxBinary) ((FxPropertyData) data)
1023: .getValue();
1024: if (!bin.isEmpty()
1025: && bin.getDefaultTranslation().getId() == binaryPreviewId)
1026: return (FxPropertyData) data;
1027: }
1028: }
1029: return null;
1030: }
1031:
1032: /**
1033: * Find the first available binary data value and return it
1034: *
1035: * @param groupData the group data entries to inspect
1036: * @return the first available binary data value or <code>null</code>
1037: */
1038: private FxPropertyData resolveFirstBinaryData(List<FxData> groupData) {
1039: FxPropertyData ret = null;
1040: for (FxData data : groupData) {
1041: if (data.isGroup())
1042: ret = resolveFirstBinaryData(((FxGroupData) data)
1043: .getChildren());
1044: if (ret != null)
1045: return ret;
1046: if (data.isProperty()
1047: && data instanceof FxPropertyData
1048: && ((FxPropertyData) data).getValue() instanceof FxBinary) {
1049: FxBinary bin = (FxBinary) ((FxPropertyData) data)
1050: .getValue();
1051: if (!bin.isEmpty())
1052: return (FxPropertyData) data;
1053: }
1054: }
1055: return ret;
1056: }
1057:
1058: /**
1059: * Find the first available image data value and return it
1060: *
1061: * @param groupData the group data entries to inspect
1062: * @return the first available image data value or <code>null</code>
1063: */
1064: private FxPropertyData resolveFirstImageData(List<FxData> groupData) {
1065: FxPropertyData ret = null;
1066: for (FxData data : groupData) {
1067: if (data.isGroup())
1068: ret = resolveFirstImageData(((FxGroupData) data)
1069: .getChildren());
1070: if (ret != null)
1071: return ret;
1072: if (data.isProperty()
1073: && data instanceof FxPropertyData
1074: && ((FxPropertyData) data).getValue() instanceof FxBinary) {
1075: FxBinary bin = (FxBinary) ((FxPropertyData) data)
1076: .getValue();
1077: if (!bin.isEmpty()
1078: && bin.getDefaultTranslation().isImage())
1079: return (FxPropertyData) data;
1080: }
1081: }
1082: return ret;
1083: }
1084:
1085: /**
1086: * Reset the preview image to show the default BinaryDescriptor.SYS_UNKNOWN image.
1087: *
1088: * @see BinaryDescriptor#SYS_UNKNOWN
1089: */
1090: public void resetBinaryPreview() {
1091: this .binaryPreviewId = BinaryDescriptor.SYS_UNKNOWN;
1092: this .binaryPreviewACL = 1;
1093: }
1094:
1095: /**
1096: * Create an independent copy of this FxContent
1097: *
1098: * @return a copy of this FxContent
1099: */
1100: public FxContent copy() {
1101: FxContent clone;
1102: clone = new FxContent(pk, typeId, relation, mandatorId, aclId,
1103: stepId, maxVersion, liveVersion, active, mainLanguage,
1104: relatedSource, relatedDestination,
1105: relatedSourcePosition, relatedDestinationPosition,
1106: lifeCycleInfo, data.copy(null), binaryPreviewId,
1107: binaryPreviewACL);
1108: return clone;
1109: }
1110:
1111: /**
1112: * Load all FxContent instances from properties of type FxReference
1113: *
1114: * @param ce ContentEngine
1115: * @throws FxApplicationException on errors
1116: */
1117: public void loadReferences(ContentEngine ce)
1118: throws FxApplicationException {
1119: List<FxReference> references = getRootGroup().getReferences(
1120: true);
1121: // int refcount = 0;
1122: // long time = System.currentTimeMillis();
1123: for (FxReference ref : references) {
1124: if (ref.isEmpty() || !ref.isValid())
1125: continue;
1126: if (ref.isMultiLanguage()) {
1127: for (long lang : ref.getTranslatedLanguages()) {
1128: ReferencedContent r = ref.getTranslation(lang);
1129: if (!r.hasContent() && r.isAccessGranted()) {
1130: r.setContent(ce.load(r));
1131: // refcount++;
1132: }
1133: }
1134: } else {
1135: if (!ref.getDefaultTranslation().hasContent()
1136: && ref.getDefaultTranslation()
1137: .isAccessGranted())
1138: ref.getDefaultTranslation().setContent(
1139: ce.load(ref.getDefaultTranslation()));
1140: // refcount++;
1141: }
1142: }
1143: // System.out.println("=> Loading " + refcount + " references took " + (System.currentTimeMillis() - time) + "[ms]");
1144: }
1145: }
|