| /* |
| * Copyright (c) 2007, Rickard Öberg. All Rights Reserved. |
| * Copyright (c) 2008, Edward Yakop. All Rights Reserved. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| */ |
| package org.apache.zest.runtime.property; |
| |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.LinkedHashMap; |
| import java.util.LinkedHashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import org.apache.zest.api.property.Property; |
| import org.apache.zest.api.property.PropertyDescriptor; |
| import org.apache.zest.api.property.PropertyWrapper; |
| import org.apache.zest.api.type.CollectionType; |
| import org.apache.zest.api.type.MapType; |
| import org.apache.zest.api.type.ValueCompositeType; |
| import org.apache.zest.api.value.ValueComposite; |
| import org.apache.zest.runtime.value.ValueInstance; |
| |
| /** |
| * {@code PropertyInstance} represents a property. |
| */ |
| public class PropertyInstance<T> |
| implements Property<T> |
| { |
| protected volatile T value; |
| protected PropertyInfo model; |
| |
| /** |
| * Construct an instance of {@code PropertyInstance} with the specified arguments. |
| * |
| * @param model The property model. This argument must not be {@code null}. |
| * @param aValue The property value. |
| */ |
| public PropertyInstance( PropertyInfo model, T aValue ) |
| { |
| this.model = model; |
| value = aValue; |
| } |
| |
| public PropertyInfo propertyInfo() |
| { |
| return model; |
| } |
| |
| /** |
| * @param model The property model. This argument must not be {@code null}. |
| */ |
| public void setPropertyInfo( PropertyInfo model ) |
| { |
| this.model = model; |
| } |
| |
| /** |
| * Returns this property value. |
| * |
| * @return This property value. |
| */ |
| @Override |
| public T get() |
| { |
| return value; |
| } |
| |
| /** |
| * Sets this property value. |
| * |
| * @param aNewValue The new value. |
| */ |
| @Override |
| public void set( T aNewValue ) |
| { |
| if( model.isImmutable() ) |
| { |
| throw new IllegalStateException( "Property [" + model.qualifiedName() + "] is immutable." ); |
| } |
| |
| model.checkConstraints( aNewValue ); |
| |
| value = aNewValue; |
| } |
| |
| /** |
| * Perform equals with {@code o} argument. |
| * <p> |
| * The definition of equals() for the Property is that if both the state and descriptor are equal, |
| * then the properties are equal. |
| * </p> |
| * |
| * @param o The other object to compare. |
| * @return Returns a {@code boolean} indicator whether this object is equals the other. |
| */ |
| @Override |
| public boolean equals( Object o ) |
| { |
| if( this == o ) |
| { |
| return true; |
| } |
| if( o == null || getClass() != o.getClass() ) |
| { |
| return false; |
| } |
| |
| Property<?> that = (Property<?>) o; |
| // Unwrap if needed |
| while( that instanceof PropertyWrapper ) |
| { |
| that = ( (PropertyWrapper) that ).next(); |
| } |
| // Descriptor equality |
| PropertyDescriptor thatDescriptor = (PropertyDescriptor) ( (PropertyInstance) that ).propertyInfo(); |
| if( !model.equals( thatDescriptor ) ) |
| { |
| return false; |
| } |
| // State equality |
| T value = get(); |
| if( value == null ) |
| { |
| return that.get() == null; |
| } |
| return value.equals( that.get() ); |
| } |
| |
| /** |
| * Calculate hash code. |
| * |
| * @return the hashcode of this instance. |
| */ |
| @Override |
| public int hashCode() |
| { |
| int hash = model.hashCode() * 19; // Descriptor |
| T value = get(); |
| if( value != null ) |
| { |
| hash += value.hashCode() * 13; // State |
| } |
| return hash; |
| } |
| |
| /** |
| * Returns the value as string. |
| * |
| * @return The value as string. |
| */ |
| @Override |
| public String toString() |
| { |
| Object value = get(); |
| return value == null ? "" : value.toString(); |
| } |
| |
| @SuppressWarnings( {"raw", "unchecked"} ) |
| public void prepareToBuild( PropertyModel propertyDescriptor ) |
| { |
| // Check if state has to be modified |
| model = propertyDescriptor.getBuilderInfo(); |
| if( propertyDescriptor.valueType() instanceof ValueCompositeType ) |
| { |
| Object value = get(); |
| if( value != null ) |
| { |
| ValueInstance.valueInstanceOf( (ValueComposite) value ).prepareToBuild(); |
| } |
| } |
| else if( propertyDescriptor.valueType() instanceof CollectionType ) |
| { |
| Object value = get(); |
| |
| if( value != null ) |
| { |
| if( value instanceof List ) |
| { |
| value = new ArrayList( (Collection) value ); |
| } |
| else if( value instanceof Set ) |
| { |
| value = new LinkedHashSet( (Collection) value ); |
| } |
| |
| // Check if items are Values |
| CollectionType collection = (CollectionType) propertyDescriptor.valueType(); |
| if( collection.collectedType() instanceof ValueCompositeType ) |
| { |
| Collection coll = (Collection) value; |
| for( Object instance : coll ) |
| { |
| ValueInstance.valueInstanceOf( (ValueComposite) instance ).prepareToBuild(); |
| } |
| } |
| |
| set( (T) value ); |
| } |
| } |
| else if( propertyDescriptor.valueType() instanceof MapType ) |
| { |
| Object value = get(); |
| |
| if( value != null ) |
| { |
| Map map = new LinkedHashMap( (Map) value ); |
| |
| // Check if keys/values are Values |
| MapType mapType = (MapType) propertyDescriptor.valueType(); |
| if( mapType.keyType() instanceof ValueCompositeType ) |
| { |
| for( Object instance : map.keySet() ) |
| { |
| ValueInstance.valueInstanceOf( (ValueComposite) instance ).prepareToBuild(); |
| } |
| } |
| if( mapType.valueType() instanceof ValueCompositeType ) |
| { |
| for( Object instance : map.values() ) |
| { |
| ValueInstance.valueInstanceOf( (ValueComposite) instance ).prepareToBuild(); |
| } |
| } |
| |
| set( (T) value ); |
| } |
| } |
| } |
| |
| @SuppressWarnings( {"raw", "unchecked"} ) |
| public void prepareBuilderState( PropertyModel propertyDescriptor ) |
| { |
| // Check if state has to be modified |
| if( propertyDescriptor.valueType() instanceof ValueCompositeType ) |
| { |
| Object value = get(); |
| if( value != null ) |
| { |
| ValueInstance.valueInstanceOf( (ValueComposite) value ).prepareBuilderState(); |
| } |
| } |
| else if( propertyDescriptor.valueType() instanceof CollectionType ) |
| { |
| T value = get(); |
| if( value != null ) |
| { |
| if( propertyDescriptor.isImmutable() ) |
| { |
| if( value instanceof List ) |
| { |
| value = (T) Collections.unmodifiableList( (List<? extends Object>) value ); |
| } |
| else if( value instanceof Set ) |
| { |
| value = (T) Collections.unmodifiableSet( (Set<? extends Object>) value ); |
| } |
| else |
| { |
| value = (T) Collections.unmodifiableCollection( (Collection<? extends Object>) value ); |
| } |
| |
| this.value = value; |
| } |
| |
| CollectionType collection = (CollectionType) propertyDescriptor.valueType(); |
| if( collection.collectedType() instanceof ValueCompositeType ) |
| { |
| Collection coll = (Collection) value; |
| for( Object instance : coll ) |
| { |
| ValueInstance.valueInstanceOf( (ValueComposite) instance ).prepareBuilderState(); |
| } |
| } |
| } |
| } |
| else if( propertyDescriptor.valueType() instanceof MapType ) |
| { |
| T value = get(); |
| |
| if( value != null ) |
| { |
| MapType mapType = (MapType) propertyDescriptor.valueType(); |
| if( mapType.keyType() instanceof ValueCompositeType ) |
| { |
| Map map = (Map) value; |
| for( Object instance : map.keySet() ) |
| { |
| ValueInstance.valueInstanceOf( (ValueComposite) instance ).prepareBuilderState(); |
| } |
| } |
| if( mapType.valueType() instanceof ValueCompositeType ) |
| { |
| Map map = (Map) value; |
| for( Object instance : map.values() ) |
| { |
| ValueInstance.valueInstanceOf( (ValueComposite) instance ).prepareBuilderState(); |
| } |
| } |
| if( propertyDescriptor.isImmutable() ) |
| { |
| value = (T) Collections.unmodifiableMap( (Map<?, ?>) value ); |
| } |
| |
| this.value = value; |
| } |
| } |
| |
| model = propertyDescriptor; |
| } |
| } |