blob: 9bd2e9d28c6ff7a4f3cf68c16431f85e0fdaf2b5 [file] [log] [blame]
/*
* 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;
}
}