/* * Copyright (C) 2005-2012 BetaCONCEPT Limited * * This file is part of Astroboa. * * Astroboa is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Astroboa 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with Astroboa. If not, see <http://www.gnu.org/licenses/>. */ package org.betaconceptframework.astroboa.model.impl; import java.io.IOException; import java.io.ObjectInputStream; import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.apache.commons.collections.CollectionUtils; import org.betaconceptframework.astroboa.api.model.ComplexCmsProperty; import org.betaconceptframework.astroboa.api.model.SimpleCmsProperty; import org.betaconceptframework.astroboa.api.model.definition.ComplexCmsPropertyDefinition; import org.betaconceptframework.astroboa.api.model.definition.SimpleCmsPropertyDefinition; import org.betaconceptframework.astroboa.api.model.exception.CmsException; import org.betaconceptframework.astroboa.api.model.exception.MultipleOccurenceException; import org.betaconceptframework.astroboa.api.model.exception.SingleOccurenceException; import org.betaconceptframework.astroboa.api.model.io.ResourceRepresentationType; import org.betaconceptframework.astroboa.context.AstroboaClientContextHolder; import org.betaconceptframework.astroboa.model.lazy.LazyLoader; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Gregory Chomatas (gchomatas@betaconcept.com) * @author Savvas Triantafyllou (striantafyllou@betaconcept.com) * */ public abstract class SimpleCmsPropertyImpl<T, D extends SimpleCmsPropertyDefinition<T>, P extends ComplexCmsProperty<? extends ComplexCmsPropertyDefinition, ? extends ComplexCmsProperty<?,?>>> extends CmsPropertyImpl<D,P> implements SimpleCmsProperty<T, D,P>, Serializable{ /** * */ private static final long serialVersionUID = 2691160236131731929L; // Real data for simple value protected List<T> values = new ArrayList<T>(); @Override public void setPropertyDefinition(D propertyDefinition) { super.setPropertyDefinition(propertyDefinition); if (this.propertyDefinition != null){ if (this.propertyDefinition.getValueType() != getValueType()) throw new CmsException("Incompatible value types. Definition "+ this.propertyDefinition.getValueType() + " , Simple Cms Property : "+ getValueType()); } } public void addSimpleTypeValue(T value){ if (propertyDefinition != null) { //In case this property is single value call method setSimpleTypeValue if (CollectionUtils.isNotEmpty(values) && isSingle()){ setSimpleTypeValue(value); return; } //In case value is null value it will not be added //in the list. Savvas was adding null values in the list and we should check whether // this is required for some reason if (value != null){ if (values == null){ values = new ArrayList<T>(); } if (propertyDefinition.isMultiple()) { values.add(value); } else { // allows only one value if (values.size() > 0) { values.set(0, value); } else { values.add(value); } } } } else { throw new CmsException("Definition of property: '" + getFullPath() + "' is null. Value will not be added"); } } /** * In cases where this property is an enumeration then all values must * be within the provided value range * @param asList */ public void checkValues() { if (CollectionUtils.isNotEmpty(values)){ if (getPropertyDefinition() == null){ throw new CmsException("Found no property definition attached to this property "+ getName()); } for (T value : values){ if (!getPropertyDefinition().isValueValid(value)){ throw new CmsException(generateMessageForInvalidValue(value)); } } } } abstract protected String generateMessageForInvalidValue(T value); public T getSimpleTypeValue() throws MultipleOccurenceException{ throwExceptionIfPropertyIsMultiple(); if (CollectionUtils.isEmpty(values)) return null; return values.get(0); } public List<T> getSimpleTypeValues() throws SingleOccurenceException{ //Throw an exception only if property is single value and list contains //more than one values. if (CollectionUtils.isNotEmpty(values) && values.size() > 1) throwExceptionIfPropertyIsSingleValue(); return values; } /** * Remove value located in index provided * @param index */ public void removeSimpleTypeValue(int index) throws SingleOccurenceException{ //Check if property is single value //and index is not 0 if (index != 0) throwExceptionIfPropertyIsSingleValue(); if (values != null){ values.remove(index); } } /** * Property is considered single valued. In any case * just update or insert ONLY the first value in list * @param value */ public void setSimpleTypeValue(T value) throws MultipleOccurenceException{ throwExceptionIfPropertyIsMultiple(); if (value != null){ if (values == null){ values = new ArrayList<T>(); } if (values.size() > 0){ values.set(0, value); } else{ values.add(value); } } else { if (values !=null) { values.clear(); } } } public void setSimpleTypeValues(List<T> values) throws SingleOccurenceException{ if (values == null){ //Throw and catch an exception to log the stack trace in order to //be able to check who provided a null list try{ throw new Exception(); } catch(Exception e){ final Logger logger = LoggerFactory.getLogger(getClass()); logger.warn("A null list is provided as values for property {}. An empty list will be created instead "+ getFullPath(), e); this.values.clear(); } } else{ this.values = values; } //Check if list contains more than value //and this property is single value if (CollectionUtils.isNotEmpty(this.values) && this.values.size() > 1) throwExceptionIfPropertyIsSingleValue(); } public boolean hasNoValues(){ return values == null || values.isEmpty(); } public boolean hasValues(){ return ! hasNoValues(); } @Override public void removeValues() { if (values != null){ values.clear(); } } //Override deserialization process to inject //property definition private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException { //Deserialize bean normally ois.defaultReadObject(); //Inject Property definition LazyLoader lazyLoader = AstroboaClientContextHolder.getLazyLoaderForClient(authenticationToken); if (lazyLoader != null){ lazyLoader.activateClientContextForAuthenticationToken(authenticationToken); setPropertyDefinition((D)lazyLoader.getDefinitionService().getCmsDefinition(fullPropertyDefinitionPath, ResourceRepresentationType.DEFINITION_INSTANCE,false)); } } @Override public boolean swapValues(int from, int to) { if (values == null){ return false; } if (from == to || from < 0 || to < 0){ return false; } try{ Collections.swap(values, from, to); resetPaths(); return true; } catch(Exception e){ //Ignore exception return false; } } @Override public boolean changePositionOfValue(int from, int to) { if (values == null) { return false; } if (from == to || from < 0 || to < 0 || from > values.size() -1 || to > values.size()) { return false; } try{ values.add(to, values.get(from)); if (from > to) { from++; } values.remove(from); resetPaths(); return true; } catch(Exception e) { //Ignore exception return false; } } @Override public T getFirstValue() { if (values == null || values.isEmpty()){ return null; } return values.get(0); } /** * Useful for retrieving all values, * regardless of the cardinality of the property. * * This way you do not have to know apriori property cardinality, * as in cases of {@link #getSimpleTypeValue()} or {@link #getSimpleTypeValues()}, * if you do not want an exception to be thrown * * @return */ public List<T> getValues(){ return values; } }