Java tutorial
/*************************************************************** * This file is part of the [fleXive](R) framework. * * Copyright (c) 1999-2014 * UCS - unique computing solutions gmbh (http://www.ucs.at) * All rights reserved * * The [fleXive](R) project is free software; you can redistribute * it and/or modify it under the terms of the GNU Lesser General Public * License version 2.1 or higher as published by the Free Software Foundation. * * The GNU Lesser General Public License can be found at * http://www.gnu.org/licenses/lgpl.html. * A copy is found in the textfile LGPL.txt and important notices to the * license from the author are found in LICENSE.txt distributed with * these libraries. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * For further information about UCS - unique computing solutions gmbh, * please see the company website: http://www.ucs.at * * For further information about [fleXive](R), please see the * project website: http://www.flexive.org * * * This copyright notice MUST APPEAR in all copies of the file! ***************************************************************/ package com.flexive.shared.content; import com.flexive.shared.FxContext; import com.flexive.shared.XPathElement; import com.flexive.shared.exceptions.FxContentExceptionCause; import com.flexive.shared.exceptions.FxInvalidParameterException; import com.flexive.shared.exceptions.FxNoAccessException; import com.flexive.shared.structure.FxPropertyAssignment; import com.flexive.shared.structure.FxStructureOption; import com.flexive.shared.structure.GroupMode; import com.flexive.shared.value.*; import com.google.common.collect.Lists; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import java.util.ArrayList; import java.util.List; /** * FxData extension for properties * * @author Markus Plesser (markus.plesser@flexive.com), UCS - unique computing solutions gmbh (http://www.ucs.at) */ public class FxPropertyData extends FxData { private static final Log LOG = LogFactory.getLog(FxPropertyData.class); private static final long serialVersionUID = -8738710689160073148L; private FxValue value; private boolean containsDefaultValue; public FxPropertyData(String xpPrefix, String alias, int index, String xPath, String xPathFull, long assignmentId, int pos, FxGroupData parent, FxValue value, boolean skipXPathSanitize) { super(xpPrefix, alias, index, xPath, xPathFull, assignmentId, pos, parent, skipXPathSanitize); this.value = value; this.containsDefaultValue = false; if (this.value != null) { setValueXPath(); } } /** * Copy constructor. * * @param other the property data instance to copy * @since 3.2.0 */ public FxPropertyData(FxPropertyData other, FxGroupData parent) { super(other, parent); this.value = other.value.copy(); this.containsDefaultValue = other.containsDefaultValue; } /** * Get the assigned value * * @return value */ public FxValue getValue() { return value; } /** * {@inheritDoc} */ @Override public boolean isProperty() { return true; } /** * {@inheritDoc} */ @Override public boolean isGroup() { return false; } /** * Get the id of the property used by the assignment * * @return id of the property used by the assignment */ public long getPropertyId() { return getPropertyAssignment().getProperty().getId(); } /** * Get the assignment cast to FxProperyAssignment * * @return property assignment * @since 3.1 */ public FxPropertyAssignment getPropertyAssignment() { return (FxPropertyAssignment) getAssignment(); } /** * Set a new value for this property data * * @param value the value to set */ @SuppressWarnings({ "ThrowableInstanceNeverThrown" }) public void setValue(FxValue value) { if (value == null) return; FxPropertyAssignment pa = (FxPropertyAssignment) this.getAssignment(); if (value.isMultiLanguage() != pa.isMultiLang()) { if (pa.isMultiLang()) throw new FxInvalidParameterException("value", "ex.content.value.invalid.multilanguage.ass.multi", this.getXPathFull()).asRuntimeException(); else throw new FxInvalidParameterException("value", "ex.content.value.invalid.multilanguage.ass.single", this.getXPathFull()).asRuntimeException(); } if (pa.getProperty().isSystemInternal()) throw new FxInvalidParameterException(pa.getAlias(), "ex.content.value.systemInternal") .setAffectedXPath(this.getXPathFull(), FxContentExceptionCause.SysInternalAttempt) .asRuntimeException(); if (!pa.isValid(value)) throw new FxInvalidParameterException("value", "ex.content.value.invalid", this.getXPathFull(), ((FxPropertyAssignment) this.getAssignment()).getProperty().getDataType(), value.getValueClass().getCanonicalName()) .setAffectedXPath(this.getXPathFull(), FxContentExceptionCause.InvalidValueDatatype) .asRuntimeException(); if (pa.hasParentGroupAssignment() && pa.getParentGroupAssignment().getMode() == GroupMode.OneOf) { //check if parent group is a one-of and already some other data set for (FxData check : this.getParent().getChildren()) { if (!check.isEmpty() && check.getAssignmentId() != this.getAssignmentId()) throw new FxInvalidParameterException("value", "ex.content.xpath.group.oneof", this.getXPathFull(), this.getParent().getXPathFull()).asRuntimeException(); } } this.containsDefaultValue = false; if (this.value == null || FxContext.get().getRunAsSystem()) { this.value = value; setValueXPath(); getParent().valueChanged(this.value.getXPath(), FxValueChangeListener.ChangeType.Update); return; } if (this.value instanceof FxNoAccess) throw new FxNoAccessException("ex.content.value.noaccess") .setAffectedXPath(this.getXPathFull(), FxContentExceptionCause.NoAccess).asRuntimeException(); if (this.value.isReadOnly()) throw new FxNoAccessException("ex.content.value.readOnly") .setAffectedXPath(this.getXPathFull(), FxContentExceptionCause.ReadOnly).asRuntimeException(); final boolean hasChangeListener = getParent().hasChangeListener(); boolean isChange = false; if (hasChangeListener) isChange = this.value == null || !value.equals(this.value); this.value = value; setValueXPath(); if (hasChangeListener && isChange) getParent().valueChanged(this.value.getXPath(), FxValueChangeListener.ChangeType.Update); } /** * {@inheritDoc} */ @Override public boolean isEmpty() { return this.value == null || this.value.isEmpty(); } /** * {@inheritDoc} */ @Override public void setEmpty() { this.value = ((FxPropertyData) this.getAssignment().createEmptyData(getParent(), getIndex(), getPos())) .getValue(); } /** * {@inheritDoc} */ @Override public boolean isRequiredPropertiesPresent() { return this.getAssignmentMultiplicity().isRequired(); } /** * Is the value contained created from a default value? * * @return value contained created from a default value */ public boolean isContainsDefaultValue() { return containsDefaultValue; } /** * Set if the value is created from a default value - <b>internal method!</b> * * @param containsDefaultValue if the value is created from a default value */ public void setContainsDefaultValue(boolean containsDefaultValue) { this.containsDefaultValue = containsDefaultValue; } /** * Apply the multiplicity to XPath and children if its a group */ @Override protected void applyIndices() { List<XPathElement> elements = XPathElement.split(this.getXPathFull()); final int lastIdx = elements.size() - 1; if (elements.get(lastIdx).getIndex() != this.getIndex()) { elements = Lists.newArrayList(elements); elements.set(lastIdx, this.getXPathElement()); setXPathFull(XPathElement.toXPath(elements)); } } /** * {@inheritDoc} */ @Override protected void setXPathFull(String xpathFull) { if (xpathFull.equals(this.XPathFull)) { return; // nop } if (parent.getChangeListener() != null) { parent.getChangeListener().onXPathChanged(this.XPathFull, xpathFull); } this.XPathFull = xpCached(xpathFull); if (this.value != null) { // apply updated XPath to FxValue (FX-920) setValueXPath(); } } /** * Check if this property is required and present in its minimal multiplicity * * @throws FxInvalidParameterException if required properties are empty */ @SuppressWarnings({ "ThrowableInstanceNeverThrown", "UnusedDeclaration" }) public void checkRequired() throws FxInvalidParameterException { if (this.getAssignmentMultiplicity().isOptional()) return; int valid = 0; for (FxData curr : getParent().getChildren()) if (curr.getAssignmentId() == this.getAssignmentId() && !curr.isEmpty()) valid++; if (valid < this.getAssignmentMultiplicity().getMin()) { // print the label of the property in the error msg if the label is present throw new FxInvalidParameterException(this.getAlias(), "ex.content.required.missing", this.getAssignment().getDisplayName(true), valid, this.getAssignmentMultiplicity().toString()) .setAffectedXPath(this.getXPathFull(), FxContentExceptionCause.RequiredViolated); } } /** * Check if the maximum length is valid (if applicable) * * @throws FxInvalidParameterException on errors */ @SuppressWarnings({ "ThrowableInstanceNeverThrown" }) public void checkMaxLength() throws FxInvalidParameterException { if (!this.getMaxLength().isSet() || value == null || value.isEmpty() || this.getMaxLength().getIntValue() == -1) return; //check for max-length compatible data types if (this.getValue() instanceof FxHTML || !(this.getValue() instanceof FxString || this.getValue() instanceof FxNumber || this.getValue() instanceof FxLargeNumber)) { LOG.warn("Values of type " + this.getValue().getClass().getSimpleName() + " are not compatible with FxStructureOption.OPTION_MAXLENGTH, check omitted!"); return; } final FxStructureOption maxLength = getMaxLength(); for (long lang : value.getTranslatedLanguages()) { if (value.getTranslation(lang).toString().length() > maxLength.getIntValue()) throw new FxInvalidParameterException(this.getAlias(), "ex.content.value.invalid.maxLength", this.getAssignment().getDisplayName(true), getMaxLength().getIntValue(), value.toString(), value.toString().length()).setAffectedXPath(this.getXPathFull(), FxContentExceptionCause.MaxlengthViolated); } } /** * {@inheritDoc} */ @Override public int getCreateableElements() { if (this.value != null && this.value instanceof FxNoAccess) return 0; return super.getCreateableElements(); } /** * {@inheritDoc} */ @Override public int getRemoveableElements() { if (this.value != null && (this.value instanceof FxNoAccess || this.value.isReadOnly())) return 0; return super.getRemoveableElements(); } /** * {@inheritDoc} */ @Override public boolean isRemoveable() { return super.isRemoveable() && // don't remove system internal assignments, except superfluous /ACL entries ("ACL".equals(getPropertyAssignment().getProperty().getName()) || !this.isSystemInternal()) && //only removeable if not null, system internal or not non accessible or readonly (!(this.value != null && (this.value instanceof FxNoAccess || this.value.isReadOnly())) || this.value == null); } /** * Returns the value of the assignment's FxStructureOption.MAXLENGTH, or -1 of the option is not set. * * @return the value of the assignment's FxStructureOption.MAXLENGTH, or -1 of the option is not set */ public FxStructureOption getMaxLength() { return getPropertyAssignment().getOption(FxStructureOption.OPTION_MAXLENGTH); } /** * Return a list of all values of this assignment. * * @param includeEmpty true to include empty (i.e. newly initialized) values * @return a list of all values of this assignment. */ public List<FxValue> getValues(boolean includeEmpty) { final List<FxValue> values = new ArrayList<FxValue>(); for (FxData data : getParent().getChildren()) { if (data.getAssignmentId() == getAssignmentId()) { final FxValue value = ((FxPropertyData) data).getValue(); if (includeEmpty || !value.isEmpty()) { values.add(value); } } } return values; } private void setValueXPath() { if (XPathFull != null) { XPathFull = xpCached(XPathElement.xpToUpperCase(XPathFull)); this.value.setXPath(xpPrefix, XPathFull); } } /** * {@inheritDoc} */ @Override public boolean equals(Object obj) { if (this == obj) return true; if (!(obj instanceof FxPropertyData)) return false; if (!super.equals(obj)) return false; FxPropertyData comp = (FxPropertyData) obj; return this.value.equals(comp.value); } /** * {@inheritDoc} */ @Override public int hashCode() { return 31 * super.hashCode() + value.hashCode(); } /** * {@inheritDoc} */ @Override FxPropertyData copy(FxGroupData parent) { return new FxPropertyData(this, parent); } }