/*!
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software
* Foundation.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
* or from the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* This program 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.
*
* Copyright (c) 2002-2013 Pentaho Corporation.. All rights reserved.
*/
package org.pentaho.pms.ui.concept.editor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang.builder.ReflectionToStringBuilder;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.pentaho.pms.schema.concept.ConceptInterface;
import org.pentaho.pms.schema.concept.ConceptPropertyInterface;
import org.pentaho.pms.schema.concept.DefaultPropertyID;
/**
* TODO synchronization!
* @author mlowery
*
*/
public class ConceptModel implements IConceptModel {
// ~ Static fields/initializers ======================================================================================
private static final Log logger = LogFactory.getLog(ConceptModel.class);
// ~ Instance fields =================================================================================================
/**
* Contains event listeners.
*/
private EventSupport eventSupport = new EventSupport();
/**
* The concept to which this class delegates.
*/
private ConceptInterface concept;
// ~ Constructors ====================================================================================================
public ConceptModel(final ConceptInterface concept) {
super();
this.concept = concept;
}
// ~ Methods =========================================================================================================
public void clearProperties() {
String[] ids = concept.getChildPropertyIDs();
for (int i = 0; i < ids.length; i++) {
removeProperty(ids[i]);
}
}
public ConceptPropertyInterface getEffectiveProperty(final String id) {
return concept.getProperty(id);
}
public Map getEffectivePropertyMap() {
return concept.getPropertyInterfaces();
}
/**
* Experimental at this point.
*/
public List getPropertySource(final String id) {
// quick check to see if id is inherited at all
if (concept.getPropertyInterfaces().containsKey(id)) {
return Collections.EMPTY_LIST;
}
// otherwise, start the recursion
List<String> path = new ArrayList<String>();
getPropertySourceInternal(concept, id, path);
return path;
}
/**
* Recursively searches related concepts (i.e. itself, its parent, its security parent, and its inherited). Returns
* the concept which has contributed the property with <code>id</code> to the leaf concept.
*/
protected void getPropertySourceInternal(final ConceptInterface concept, final String id, final List<String> path) {
Map childPropertiesMap = concept.getChildPropertyInterfaces();
if (childPropertiesMap.containsKey(id)) {
path.add("source");
} else if (DefaultPropertyID.SECURITY.getId().equals(id) && concept.hasSecurityParentConcept()
&& concept.getSecurityPropertyInterfaces().containsKey(id)) {
path.add("security");
getPropertySourceInternal(concept.getSecurityParentInterface(), id, path);
} else if (concept.hasParentConcept() && concept.getParentPropertyInterfaces().containsKey(id)) {
path.add("parent");
getPropertySourceInternal(concept.getParentInterface(), id, path);
} else if (concept.hasInheritedConcept() && concept.getInheritedPropertyInterfaces().containsKey(id)) {
path.add("inherited");
getPropertySourceInternal(concept.getInheritedInterface(), id, path);
} else {
// a default property perhaps?
path.add("default");
}
}
public ConceptPropertyInterface getProperty(final String id, final int relType) {
switch (relType) {
case IConceptModel.REL_THIS: {
return concept.getChildProperty(id);
}
case IConceptModel.REL_SECURITY: {
return concept.getSecurityProperty(id);
}
case IConceptModel.REL_PARENT: {
return concept.getParentProperty(id);
}
case IConceptModel.REL_INHERITED: {
return concept.getInheritedProperty(id);
}
default: {
throw new IllegalArgumentException("illegal argument");
}
}
}
public Set getPropertyIds(final int relType) {
String[] ids;
switch (relType) {
case IConceptModel.REL_THIS: {
ids = concept.getChildPropertyIDs();
break;
}
case IConceptModel.REL_SECURITY: {
ids = concept.getSecurityParentInterface().getPropertyIDs();
break;
}
case IConceptModel.REL_PARENT: {
ids = concept.getParentInterface().getPropertyIDs();
break;
}
case IConceptModel.REL_INHERITED: {
ids = concept.getInheritedInterface().getPropertyIDs();
break;
}
default: {
throw new IllegalArgumentException("illegal argument");
}
}
return new HashSet<String>(Arrays.asList(ids));
}
public Map getPropertyMap(final int relType) {
switch (relType) {
case IConceptModel.REL_THIS: {
return concept.getChildPropertyInterfaces();
}
case IConceptModel.REL_SECURITY: {
return concept.getSecurityPropertyInterfaces();
}
case IConceptModel.REL_PARENT: {
return concept.getParentPropertyInterfaces();
}
case IConceptModel.REL_INHERITED: {
return concept.getInheritedPropertyInterfaces();
}
default: {
throw new IllegalArgumentException("illegal argument");
}
}
}
public boolean hasRelatedConcept(final int relType) {
switch (relType) {
case IConceptModel.REL_THIS: {
return true;
}
case IConceptModel.REL_SECURITY: {
return concept.hasSecurityParentConcept();
}
case IConceptModel.REL_PARENT: {
return concept.hasParentConcept();
}
case IConceptModel.REL_INHERITED: {
return concept.hasInheritedConcept();
}
default: {
throw new IllegalArgumentException("illegal argument");
}
}
}
public void setProperty(final ConceptPropertyInterface property) {
/*
* Logic goes like this:
* if child prop already exists, set old value to value of existing child prop and fire change event
* else if the prop is from inherited/parent/security, set the old value to the inherited/parent/security value and
* fire override event
* else, the prop is a new child prop so fire add event
*/
int type = -1;
ConceptPropertyInterface oldValue = null;
if (concept.getChildPropertyInterfaces().containsKey(property.getId())) {
type = PropertyExistenceModificationEvent.CHANGE_PROPERTY;
oldValue = (ConceptPropertyInterface) concept.getChildPropertyInterfaces().get(property.getId());
} else if (null != concept.getInheritedProperty(property.getId())
|| null != concept.getParentProperty(property.getId())
|| (null != concept.getSecurityProperty(property.getId()) && DefaultPropertyID.SECURITY.getId().equals(
property.getId()))) {
type = PropertyExistenceModificationEvent.OVERRIDE_PROPERTY;
// get old value which is the value from parent/inherited/security; order matters here!
if (null != concept.getSecurityProperty(property.getId())
&& DefaultPropertyID.SECURITY.getId().equals(property.getId())) {
oldValue = (ConceptPropertyInterface) concept.getSecurityProperty(property.getId());
} else if (null != concept.getParentProperty(property.getId())) {
oldValue = concept.getParentProperty(property.getId());
} else {
oldValue = concept.getInheritedProperty(property.getId());
}
} else {
type = PropertyExistenceModificationEvent.ADD_PROPERTY;
}
concept.addProperty(property);
PropertyExistenceModificationEvent e = new PropertyExistenceModificationEvent(this, property.getId(), type,
oldValue, property);
fireConceptModificationEvent(e);
}
public void removeProperty(final String id) {
/*
* Logic goes like this:
* if property was overriding a property from parent/inherited/security, the new value in fired event will be the
* value from parent/inherited/security and event will be an "inherit" event (the opposite of "override")
* else new value is null and event will be a remove event
*/
ConceptPropertyInterface oldValue = (ConceptPropertyInterface) concept.getChildProperty(id);
int type = -1;
ConceptPropertyInterface newValue = null;
if (null == oldValue) {
// property with given id does not exist
return;
}
if (null != concept.getInheritedProperty(id) || null != concept.getParentProperty(id)
|| (null != concept.getSecurityProperty(id) && DefaultPropertyID.SECURITY.getId().equals(id))) {
type = PropertyExistenceModificationEvent.INHERIT_PROPERTY;
if (null != concept.getSecurityProperty(id) && DefaultPropertyID.SECURITY.getId().equals(id)) {
newValue = (ConceptPropertyInterface) concept.getSecurityProperty(id);
} else if (null != concept.getParentProperty(id)) {
newValue = concept.getParentProperty(id);
} else {
newValue = concept.getInheritedProperty(id);
}
} else {
type = PropertyExistenceModificationEvent.REMOVE_PROPERTY;
}
concept.getChildPropertyInterfaces().remove(id);
PropertyModificationEvent e = new PropertyExistenceModificationEvent(this, id, type, oldValue, newValue);
fireConceptModificationEvent(e);
}
public void setRelatedConcept(final ConceptInterface relatedConcept, final int relType) {
if (null == relatedConcept) {
removeRelatedConcept(relType);
}
ConceptInterface oldValue = null;
int type = -1;
switch (relType) {
case IConceptModel.REL_THIS: {
throw new IllegalArgumentException("REL_THIS is an illegal argument");
}
case IConceptModel.REL_SECURITY: {
oldValue = concept.getSecurityParentInterface();
concept.setSecurityParentInterface(relatedConcept);
break;
}
case IConceptModel.REL_PARENT: {
oldValue = concept.getParentInterface();
concept.setParentInterface(relatedConcept);
break;
}
case IConceptModel.REL_INHERITED: {
oldValue = concept.getInheritedInterface();
concept.setInheritedInterface(relatedConcept);
break;
}
default: {
throw new IllegalArgumentException("illegal argument");
}
}
if (null != oldValue) {
type = RelatedConceptModificationEvent.CHANGE_RELATED_CONCEPT;
} else {
type = RelatedConceptModificationEvent.ADD_RELATED_CONCEPT;
}
RelatedConceptModificationEvent e = new RelatedConceptModificationEvent(this, type, relType, oldValue,
relatedConcept);
fireConceptModificationEvent(e);
}
public void removeRelatedConcept(final int relType) {
ConceptInterface oldValue = null;
int type = RelatedConceptModificationEvent.REMOVE_RELATED_CONCEPT;
switch (relType) {
case IConceptModel.REL_THIS: {
throw new IllegalArgumentException("REL_THIS is an illegal argument");
}
case IConceptModel.REL_SECURITY: {
oldValue = concept.getSecurityParentInterface();
concept.setSecurityParentInterface(null);
break;
}
case IConceptModel.REL_PARENT: {
oldValue = concept.getParentInterface();
concept.setParentInterface(null);
break;
}
case IConceptModel.REL_INHERITED: {
oldValue = concept.getInheritedInterface();
concept.setInheritedInterface(null);
break;
}
default: {
throw new IllegalArgumentException("REL_THIS is an illegal argument");
}
}
if (null != oldValue) {
RelatedConceptModificationEvent e = new RelatedConceptModificationEvent(this, type, relType, oldValue, null);
fireConceptModificationEvent(e);
}
}
public void addConceptModificationListener(final IConceptModificationListener conceptModelListener) {
eventSupport.addListener(conceptModelListener);
}
public void removeConceptModificationListener(final IConceptModificationListener conceptModelListener) {
eventSupport.removeListener(conceptModelListener);
}
protected void fireConceptModificationEvent(final ConceptModificationEvent e) {
for (Iterator iter = eventSupport.getListeners().iterator(); iter.hasNext();) {
IConceptModificationListener target = (IConceptModificationListener) iter.next();
target.conceptModified(e);
}
}
/**
* Returns whether or not this property can be overridden. This can return false if the property is a "default"
* property.
*/
public boolean canOverride(final String id) {
ConceptPropertyInterface propFromThis = this.getProperty(id, IConceptModel.REL_THIS);
ConceptPropertyInterface propFromSecurity = this.getProperty(id, IConceptModel.REL_SECURITY);
ConceptPropertyInterface propFromInherited = this.getProperty(id, IConceptModel.REL_INHERITED);
ConceptPropertyInterface propFromParent = this.getProperty(id, IConceptModel.REL_PARENT);
if (null != propFromThis && propFromThis.isRequired()) {
return false;
} else if (null != propFromInherited || null != propFromParent) {
return true;
} else if (null != propFromSecurity && DefaultPropertyID.SECURITY.getId().equals(id)) {
return true;
} else {
return false;
}
}
public boolean isOverridden(final String id) {
return null != getProperty(id, IConceptModel.REL_THIS) && canOverride(id);
}
public void setPropertyValue(final String id, final Object value) {
ConceptPropertyInterface prop = concept.getChildProperty(id);
if (null == prop) {
if (logger.isWarnEnabled()) {
logger.warn("property with id \"" + id + "\" does not exist in the concept; ignoring");
}
return;
}
Object oldValue = prop.getValue();
prop.setValue(value);
fireConceptModificationEvent(new PropertyValueModificationEvent(this, id, oldValue, value));
}
public String toString() {
return new ReflectionToStringBuilder(this).toString();
}
public int getPropertyContributor(final String id) {
Map childPropertiesMap = concept.getChildPropertyInterfaces();
if (childPropertiesMap.containsKey(id)) {
return IConceptModel.REL_THIS;
} else if (DefaultPropertyID.SECURITY.getId().equals(id) && concept.hasSecurityParentConcept()
&& concept.getSecurityPropertyInterfaces().containsKey(id)) {
return IConceptModel.REL_SECURITY;
} else if (concept.hasParentConcept() && concept.getParentPropertyInterfaces().containsKey(id)) {
return IConceptModel.REL_PARENT;
} else if (concept.hasInheritedConcept() && concept.getInheritedPropertyInterfaces().containsKey(id)) {
return IConceptModel.REL_INHERITED;
} else {
return -1;
}
}
}