/*******************************************************************************
* Copyright (c) 1998, 2015 Oracle and/or its affiliates. All rights reserved.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
* which accompanies this distribution.
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Oracle - initial API and implementation from Oracle TopLink
******************************************************************************/
package org.eclipse.persistence.tools.workbench.mappingsmodel.descriptor;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;
import org.eclipse.persistence.tools.workbench.mappingsmodel.MWDataField;
import org.eclipse.persistence.tools.workbench.mappingsmodel.ProblemConstants;
import org.eclipse.persistence.tools.workbench.mappingsmodel.meta.MWClass;
import org.eclipse.persistence.tools.workbench.mappingsmodel.meta.MWTypeDeclaration;
import org.eclipse.persistence.tools.workbench.utility.ClassTools;
import org.eclipse.persistence.tools.workbench.utility.iterators.CloneIterator;
import org.eclipse.persistence.tools.workbench.utility.iterators.NullIterator;
import org.eclipse.persistence.tools.workbench.utility.node.Node;
import org.eclipse.persistence.descriptors.DescriptorEvent;
import org.eclipse.persistence.descriptors.InheritancePolicy;
import org.eclipse.persistence.exceptions.ConversionException;
import org.eclipse.persistence.internal.helper.ConversionManager;
import org.eclipse.persistence.oxm.XMLDescriptor;
import org.eclipse.persistence.oxm.mappings.XMLCompositeCollectionMapping;
import org.eclipse.persistence.oxm.mappings.XMLCompositeObjectMapping;
import org.eclipse.persistence.oxm.mappings.XMLDirectMapping;
//TODO this should be refactored further with a policy for classNameIsIndicator ~kfm
public abstract class MWClassIndicatorFieldPolicy extends MWAbstractClassIndicatorPolicy {
private volatile boolean classNameIsIndicator;
public final static String CLASS_NAME_IS_INDICATOR_PROPERTY = "classNameIsIndicator";
private transient volatile MWTypeDeclaration indicatorType;
public final static String INDICATOR_TYPE_PROPERTY = "indicatorType";
private Collection classIndicatorValues;
public final static String CLASS_INDICATOR_VALUES_COLLECTION = "classIndicatorValues";
// only used by 4.5 projects
private volatile Class legacyIndicatorType;
private volatile ConversionManager conversionManager; // used to convert objects to various classes
//These are only used for backward compatibility of 3.5 through 4.5 projects
//Should not be used anywhere else
public static Class[] ALLOWED_INDICATOR_TYPES = new Class[] {String.class, Integer.class, Boolean.class, Long.class};
public static Class DEFAULT_INDICATOR_TYPE = String.class;
// *************** static methods **************
public static XMLDescriptor buildDescriptor() {
XMLDescriptor descriptor = new XMLDescriptor();
descriptor.setJavaClass(MWClassIndicatorFieldPolicy.class);
descriptor.getInheritancePolicy().setParentClass(MWAbstractClassIndicatorPolicy.class);
XMLDirectMapping classNameIndicatorMapping = (XMLDirectMapping)descriptor.addDirectMapping("classNameIsIndicator", "class-name-is-indicator/text()");
classNameIndicatorMapping.setNullValue(Boolean.FALSE);
XMLCompositeObjectMapping indicatorTypeMapping = new XMLCompositeObjectMapping();
indicatorTypeMapping.setAttributeName("indicatorType");
indicatorTypeMapping.setReferenceClass(MWTypeDeclaration.class);
indicatorTypeMapping.setXPath("indicator-type");
descriptor.addMapping(indicatorTypeMapping);
XMLCompositeCollectionMapping classIndicatorValuesMapping = new XMLCompositeCollectionMapping();
classIndicatorValuesMapping.setAttributeName("classIndicatorValues");
classIndicatorValuesMapping.setGetMethodName("getIndicatorValuesForTopLink");
classIndicatorValuesMapping.setSetMethodName("setIndicatorValuesForTopLink");
classIndicatorValuesMapping.setReferenceClass(MWClassIndicatorValue.class);
classIndicatorValuesMapping.setXPath("class-indicator-values/class-indicator-value");
descriptor.addMapping(classIndicatorValuesMapping);
return descriptor;
}
// *************** constructors **************
protected MWClassIndicatorFieldPolicy() {
super();
}
protected MWClassIndicatorFieldPolicy(MWClassIndicatorPolicy.Parent parent) {
this(parent, NullIterator.instance());
}
protected MWClassIndicatorFieldPolicy(MWClassIndicatorPolicy.Parent parent, Iterator descriptorsAvailableForIndication) {
super(parent);
setDescriptorsAvailableForIndicatorDictionary(descriptorsAvailableForIndication);
}
/**
* initialize persistent state
*/
protected void initialize(Node parent) {
super.initialize(parent);
this.indicatorType = new MWTypeDeclaration(this, this.typeFor(DEFAULT_INDICATOR_TYPE));
this.classIndicatorValues = new Vector();
this.classNameIsIndicator = false;
}
protected void addChildrenTo(List children) {
super.addChildrenTo(children);
synchronized (this.classIndicatorValues) { children.addAll(this.classIndicatorValues); }
if (this.indicatorType != null) {
children.add(this.indicatorType);
}
}
// *************** accessors **************
public String getType() {
return CLASS_INDICATOR_FIELD_TYPE;
}
public abstract MWDataField getField();
public MWClassIndicatorValue addIndicator(Object value, MWMappingDescriptor descriptor) {
MWClassIndicatorValue indicator = new MWClassIndicatorValue(this, descriptor, value);
this.classIndicatorValues.add(indicator);
fireItemAdded(CLASS_INDICATOR_VALUES_COLLECTION, indicator);
return indicator;
}
public void removeIndicator(MWClassIndicatorValue indicator) {
this.removeItemFromCollection(indicator, this.classIndicatorValues, CLASS_INDICATOR_VALUES_COLLECTION);
}
public void removeIndicatorFor(MWDescriptor descriptor) {
for (Iterator stream = this.classIndicatorValues(); stream.hasNext(); ) {
MWClassIndicatorValue value = (MWClassIndicatorValue) stream.next();
if (value.getDescriptorValue() == descriptor) {
this.removeIndicator(value);
return;
}
}
}
public void setClassNameIsIndicator(boolean classNameIsIndicator) {
boolean oldValue = this.classNameIsIndicator;
this.classNameIsIndicator = classNameIsIndicator;
if (oldValue != classNameIsIndicator) {
if (classNameIsIndicator) {
setIndicatorType(null);
}
else {
setIndicatorType(new MWTypeDeclaration(this, typeNamed(DEFAULT_INDICATOR_TYPE.getName())));
setDescriptorsAvailableForIndicatorDictionary(((MWDescriptorInheritancePolicy) getParent()).getAllDescriptorsAvailableForIndicatorDictionary().iterator());
}
}
firePropertyChanged(CLASS_NAME_IS_INDICATOR_PROPERTY, oldValue, classNameIsIndicator);
}
public void setIndicatorType(MWTypeDeclaration newIndicatorType) {
MWTypeDeclaration oldValue = getIndicatorType();
this.indicatorType = newIndicatorType;
convertValues();
firePropertyChanged(INDICATOR_TYPE_PROPERTY, oldValue, newIndicatorType);
}
public boolean classNameIsIndicator() {
return this.classNameIsIndicator;
}
/**
* Convert the string to an object of the specified class.
* If there are any problems, do nothing - the value will not be built.
*/
public Object buildIndicatorValueFromString(String valueString)
{
try
{
Class javaClass = ClassTools.classForTypeDeclaration(getIndicatorType().typeName(), getIndicatorType().getDimensionality());
return this.getConversionManager().convertObject(valueString, javaClass);
}
catch (ClassNotFoundException ex)
{
// it's very unlikely this will happen since
// we try to restrict the class to common classes (e.g. String, Date)
}
throw ConversionException.couldNotBeConverted(valueString, null);
}
public void clearIndicatorValues() {
Iterator values = classIndicatorValues();
while (values.hasNext()) {
MWClassIndicatorValue value = (MWClassIndicatorValue) values.next();
removeIndicator(value);
}
}
public void convertValues() {
if (getIndicatorType() == null) {
clearIndicatorValues();
}
for (Iterator i = classIndicatorValues(); i.hasNext(); ) {
MWClassIndicatorValue value = (MWClassIndicatorValue) i.next();
Object before = value.getIndicatorValue();
if (before != null) {
try {
Object after = getConversionManager().convertObject(before, ClassTools.classForTypeDeclaration(getIndicatorType().typeName(), getIndicatorType().getDimensionality()));
value.setIndicatorValue(after);
}
catch (ClassNotFoundException ex) {
throw new RuntimeException(ex);
}
catch (ConversionException ex) {
value.setIndicatorValue(null);
}
}
}
}
public MWClassIndicatorValue getClassIndicatorValueForDescriptor(MWMappingDescriptor descriptor)
{
Iterator i = classIndicatorValues();
while (i.hasNext()) {
MWClassIndicatorValue indicatorValue = (MWClassIndicatorValue) i.next();
if (indicatorValue.getDescriptorValue().equals(descriptor)) {
return indicatorValue;
}
}
return null;
}
public Iterator classIndicatorValues() {
return new CloneIterator(this.classIndicatorValues);
}
public int classIndicatorValuesSize() {
return this.classIndicatorValues.size();
}
public ConversionManager getConversionManager() {
if (this.conversionManager == null) {
this.conversionManager = new ConversionManager();
}
return this.conversionManager;
}
public MWDescriptor getDescriptorForIndicator(Object indicator) {
Iterator i = includedClassIndicatorValues();
while (i.hasNext()) {
MWClassIndicatorValue indicatorValue = (MWClassIndicatorValue) i.next();
if (indicatorValue.getIndicatorValue() != null && indicatorValue.getIndicatorValue().equals(indicator)) {
return indicatorValue.getDescriptorValue();
}
}
return null;
}
public Object getIndicatorForDescriptor(MWMappingDescriptor descriptor) {
Iterator i = classIndicatorValues();
while (i.hasNext()) {
MWClassIndicatorValue indicatorValue = (MWClassIndicatorValue) i.next();
if (indicatorValue.getDescriptorValue().equals(descriptor)) {
return indicatorValue.getIndicatorValue();
}
}
return null;
}
public MWTypeDeclaration getIndicatorType() {
return this.indicatorType;
}
public Iterator includedClassIndicatorValues() {
return getIndicatorValuesForTopLink().iterator();
}
public boolean isRepeatedIndicatorValue(Object indicatorValue) {
Iterator values = classIndicatorValues();
while (values.hasNext()) {
Object value = ((MWClassIndicatorValue) values.next()).getIndicatorValue();
if (value != null && value.equals(indicatorValue)) {
return true;
}
}
return false;
}
public void rebuildClassIndicatorValues(Collection descriptors) {
for (Iterator i = classIndicatorValues(); i.hasNext();) {
MWClassIndicatorValue indicatorValue = (MWClassIndicatorValue) i.next();
if (!descriptors.contains(indicatorValue.getDescriptorValue()) ){
removeIndicator(indicatorValue);
}
}
setDescriptorsAvailableForIndicatorDictionary(descriptors.iterator());
}
public void setDescriptorsAvailableForIndicatorDictionary(Iterator descriptors) {
if (classNameIsIndicator()) {
setIndicatorType(null);
return;
}
while (descriptors.hasNext()) {
MWMappingDescriptor descriptor = (MWMappingDescriptor) descriptors.next();
if (getClassIndicatorValueForDescriptor(descriptor) == null)
addIndicator(null, descriptor);
}
}
public void setDescriptorsAvailableForIndicatorDictionaryForTopLink(Iterator descriptors) {
if (classNameIsIndicator()) {
setIndicatorType(null);
return;
}
while (descriptors.hasNext()) {
MWMappingDescriptor descriptor = (MWMappingDescriptor) descriptors.next();
if (getClassIndicatorValueForDescriptor(descriptor) == null) {
MWClassIndicatorValue value = addIndicator(null, descriptor);
value.setInclude(false);
}
}
}
public void resetDescriptorAvailableForIndication(Iterator descriptors) {
Iterator currentIndicators = classIndicatorValues();
while (currentIndicators.hasNext()) {
MWClassIndicatorValue value = (MWClassIndicatorValue) currentIndicators.next();
removeIndicator(value);
}
setDescriptorsAvailableForIndicatorDictionary(descriptors);
}
protected abstract boolean fieldSpecified();
//*************** Problem Handling *************
public void checkClassIndicatorField(List newProblems) {
if ( ! this.fieldSpecified()) {
newProblems.add(this.buildProblem(ProblemConstants.NO_CLASS_INDICATOR_FOR_ROOT_CLASS));
}
}
// ************* Runtime Conversion ***********
public void adjustRuntimeInheritancePolicy(InheritancePolicy runtimeInheritancePolicy) {
Iterator indicatorValues = includedClassIndicatorValues();
while (indicatorValues.hasNext()) {
Object value = ((MWClassIndicatorValue)indicatorValues.next()).getIndicatorValue();
if (value != null)
runtimeInheritancePolicy.addClassNameIndicator(getDescriptorForIndicator(value).getMWClass().fullName(), value);
}
runtimeInheritancePolicy.setShouldUseClassNameAsIndicator(classNameIsIndicator());
}
// ************* TopLink only methods ***********
public void postProjectBuild() {
super.postProjectBuild();
// convert the values because they are converted to strings when written out
this.convertValues();
}
private List getIndicatorValuesForTopLink() {
List topLinkValues = new ArrayList();
for (Iterator stream = classIndicatorValues(); stream.hasNext(); ) {
MWClassIndicatorValue value = (MWClassIndicatorValue) stream.next();
if (value.isInclude()) {
topLinkValues.add(value);
}
}
return topLinkValues;
}
private void setIndicatorValuesForTopLink(List indicatorValues) {
this.classIndicatorValues = indicatorValues;
}
}