/***************************************************************************
* Copyright (C) 2009-2011 by Fabrizio Montesi <famontesi@gmail.com> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Library General Public License as *
* published by the Free Software Foundation; either version 2 of the *
* License, or (at your option) any later version. *
* *
* 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 General Public License for more details. *
* *
* You should have received a copy of the GNU Library General Public *
* License along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
* *
* For details about the authors of this software, see the AUTHORS file. *
***************************************************************************/
package jolie.runtime.typing;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import jolie.lang.NativeType;
import jolie.runtime.Value;
import jolie.runtime.ValueVector;
import jolie.util.Range;
class TypeImpl extends Type
{
private final Range cardinality;
private final NativeType nativeType;
private final Set< Entry< String, Type > > subTypeSet;
private final Set< String > subTypeKeySet;
public TypeImpl(
NativeType nativeType,
Range cardinality,
boolean undefinedSubTypes,
Map< String, Type > subTypes
) {
this.nativeType = nativeType;
this.cardinality = cardinality;
if ( undefinedSubTypes ) {
subTypeSet = null;
subTypeKeySet = null;
} else {
subTypeSet = subTypes.entrySet();
subTypeKeySet = subTypes.keySet();
}
}
protected Set< Entry< String, Type > > subTypeSet()
{
return subTypeSet;
}
protected Range cardinality()
{
return cardinality;
}
public void cutChildrenFromValue( Value value )
{
if ( subTypeKeySet != null ) {
for( String childName : subTypeKeySet ) {
value.children().remove( childName );
}
}
}
protected Value cast( Value value, StringBuilder pathBuilder )
throws TypeCastingException
{
castNativeType( value, pathBuilder );
if ( subTypeSet != null ) {
for( Entry< String, Type > entry : subTypeSet ) {
castSubType( entry.getKey(), entry.getValue(), value, new StringBuilder( pathBuilder ) );
}
}
return value;
}
private void castSubType( String typeName, Type type, Value value, StringBuilder pathBuilder )
throws TypeCastingException
{
pathBuilder.append( '.' );
pathBuilder.append( typeName );
boolean hasChildren = value.hasChildren( typeName );
if ( hasChildren == false && type.cardinality().min() > 0 ) {
throw new TypeCastingException( "Undefined required child node: " + pathBuilder.toString() );
} else if ( hasChildren ) {
ValueVector vector = value.getChildren( typeName );
int size = vector.size();
if ( type.cardinality().min() > size || type.cardinality().max() < size ) {
throw new TypeCastingException(
"Child node " + pathBuilder.toString() + " has a wrong number of occurencies. Permitted range is [" +
type.cardinality().min() + "," + type.cardinality().max() + "], found " + size
);
}
for( Value v : vector ) {
type.cast( v, pathBuilder );
}
}
}
protected void check( Value value, StringBuilder pathBuilder )
throws TypeCheckingException
{
if ( checkNativeType( value, nativeType ) == false ) {
throw new TypeCheckingException( "Invalid native type for node " + pathBuilder.toString() + ": expected " + nativeType + ", found " + (( value.valueObject() == null ) ? "void" : value.valueObject().getClass().getName()) );
}
if ( subTypeSet != null ) {
for( Entry< String, Type > entry : subTypeSet ) {
checkSubType( entry.getKey(), entry.getValue(), value, new StringBuilder( pathBuilder ) );
}
// TODO make this more performant
for( String childName : value.children().keySet() ) {
if ( subTypeKeySet.contains( childName ) == false ) {
throw new TypeCheckingException( "Unexpected child node: " + pathBuilder.toString() + "." + childName );
}
}
}
}
private void checkSubType( String typeName, Type type, Value value, StringBuilder pathBuilder )
throws TypeCheckingException
{
pathBuilder.append( '.' );
pathBuilder.append( typeName );
boolean hasChildren = value.hasChildren( typeName );
if ( hasChildren == false && type.cardinality().min() > 0 ) {
throw new TypeCheckingException( "Undefined required child node: " + pathBuilder.toString() );
} else if ( hasChildren ) {
ValueVector vector = value.getChildren( typeName );
int size = vector.size();
if ( type.cardinality().min() > size || type.cardinality().max() < size ) {
throw new TypeCheckingException(
"Child node " + pathBuilder.toString() + " has a wrong number of occurencies. Permitted range is [" +
type.cardinality().min() + "," + type.cardinality().max() + "], found " + size
);
}
for( Value v : vector ) {
type.check( v, pathBuilder );
}
}
}
private void castNativeType( Value value, StringBuilder pathBuilder )
throws TypeCastingException
{
if ( checkNativeType( value, nativeType ) == false ) {
// ANY is not handled, because checkNativeType returns true for it anyway
if ( nativeType == NativeType.DOUBLE ) {
try {
value.setValue( value.doubleValueStrict() );
} catch( TypeCastingException e ) {
throw new TypeCastingException( "Cannot cast node value to " + nativeType.id() + ": " + pathBuilder.toString() );
}
} else if ( nativeType == NativeType.BYTE ) {
try {
value.setValue( value.byteValueStrict() );
} catch( TypeCastingException e ) {
throw new TypeCastingException( "Cannot cast node value to " + nativeType.id() + ": " + pathBuilder.toString() );
}
} else if ( nativeType == NativeType.INT16 ) {
try {
value.setValue( value.int16ValueStrict() );
} catch( TypeCastingException e ) {
throw new TypeCastingException( "Cannot cast node value to " + nativeType.id() + ": " + pathBuilder.toString() );
}
} else if ( nativeType == NativeType.UINT16 ) {
try {
value.setValue( value.uInt16ValueStrict() );
} catch( TypeCastingException e ) {
throw new TypeCastingException( "Cannot cast node value to " + nativeType.id() + ": " + pathBuilder.toString() );
}
} else if ( nativeType == NativeType.INT ) {
try {
value.setValue( value.intValueStrict() );
} catch( TypeCastingException e ) {
throw new TypeCastingException( "Cannot cast node value to " + nativeType.id() + ": " + pathBuilder.toString() );
}
} else if ( nativeType == NativeType.UINT32 ) {
try {
value.setValue( value.uInt32ValueStrict());
} catch( TypeCastingException e ) {
throw new TypeCastingException( "Cannot cast node value to " + nativeType.id() + ": " + pathBuilder.toString() );
}
} else if ( nativeType == NativeType.LONG ) {
try {
value.setValue( value.longValueStrict() );
} catch( TypeCastingException e ) {
throw new TypeCastingException( "Cannot cast node value to " + nativeType.id() + ": " + pathBuilder.toString() );
}
} else if ( nativeType == NativeType.UINT64 ) {
try {
value.setValue( value.uInt64ValueStrict());
} catch( TypeCastingException e ) {
throw new TypeCastingException( "Cannot cast node value to " + nativeType.id() + ": " + pathBuilder.toString() );
}
} else if ( nativeType == NativeType.BOOL ) {
try {
value.setValue( value.boolValueStrict() );
} catch( TypeCastingException e ) {
throw new TypeCastingException( "Cannot cast node value to " + nativeType.id() + ": " + pathBuilder.toString() );
}
} else if ( nativeType == NativeType.STRING ) {
try {
value.setValue( value.strValueStrict() );
} catch( TypeCastingException e ) {
throw new TypeCastingException( "Cannot cast node value to " + nativeType.id() + ": " + pathBuilder.toString() );
}
} else if ( nativeType == NativeType.VOID ) {
if ( value.valueObject() != null ) {
throw new TypeCastingException(
"Expected " + NativeType.VOID.id() + ", found " +
value.valueObject().getClass().getSimpleName() +
": " + pathBuilder.toString()
);
}
} else if ( nativeType == NativeType.RAW ) {
try {
value.setValue( value.byteArrayValueStrict() );
} catch( TypeCastingException e ) {
throw new TypeCastingException( "Cannot cast node value to " + nativeType.id() + ": " + pathBuilder.toString() );
}
} else {
throw new TypeCastingException(
"Expected " + nativeType.id() + ", found " +
value.valueObject().getClass().getSimpleName() +
": " + pathBuilder.toString()
);
}
}
}
private boolean checkNativeType( Value value, NativeType nativeType )
{
if ( nativeType == NativeType.ANY ) {
return true;
} else if ( nativeType == NativeType.DOUBLE ) {
return value.isDouble() || value.isInt();
} else if ( nativeType == NativeType.LONG ) {
return value.isInt() || value.isLong();
} else if ( nativeType == NativeType.BOOL ) {
return value.isBool();
} else if ( nativeType == NativeType.INT ) {
return value.isInt();
} else if ( nativeType == NativeType.STRING ) {
return value.isString();
} else if ( nativeType == NativeType.VOID ) {
return value.valueObject() == null;
} else if ( nativeType == NativeType.RAW ) {
return value.isByteArray();
} else if ( nativeType == NativeType.UINT32) {
return value.isUInt32();
} else if ( nativeType == NativeType.UINT16) {
return value.isUInt16();
} else if ( nativeType == NativeType.INT16) {
return value.isInt16();
} else if ( nativeType == NativeType.UINT64) {
return value.isUInt64();
} else if ( nativeType == NativeType.BYTE) {
return value.isByte();
}
return false;
}
protected NativeType nativeType()
{
return nativeType;
}
}
/**
*
* @author Fabrizio Montesi
*/
public abstract class Type implements Cloneable
{
public static final Type UNDEFINED =
Type.create( NativeType.ANY, new Range( 0, Integer.MAX_VALUE ), true, null );
public static Type create(
NativeType nativeType,
Range cardinality,
boolean undefinedSubTypes,
Map< String, Type > subTypes
) {
return new TypeImpl( nativeType, cardinality, undefinedSubTypes, subTypes );
}
public static TypeLink createLink( String linkedTypeName, Range cardinality )
{
return new TypeLink( linkedTypeName, cardinality );
}
public static Type merge( Type t1, Type t2 )
{
NativeType nativeType = t1.nativeType();
Range cardinality = t1.cardinality();
Map< String, Type > subTypes = new HashMap< String, Type >();
for( Entry< String, Type > entry : t1.subTypeSet() ) {
subTypes.put( entry.getKey(), entry.getValue() );
}
if ( t2 != null ) {
for( Entry< String, Type > entry : t2.subTypeSet() ) {
subTypes.put( entry.getKey(), entry.getValue() );
}
}
return create( nativeType, cardinality, false, subTypes );
}
public void check( Value value )
throws TypeCheckingException
{
check( value, new StringBuilder( "#Message" ) );
}
public Value cast( Value value )
throws TypeCastingException
{
return cast( value, new StringBuilder( "#Message" ) );
}
public abstract void cutChildrenFromValue( Value value );
protected abstract NativeType nativeType();
protected abstract Range cardinality();
protected abstract Set< Entry< String, Type > > subTypeSet();
protected abstract void check( Value value, StringBuilder pathBuilder )
throws TypeCheckingException;
protected abstract Value cast( Value value, StringBuilder pathBuilder )
throws TypeCastingException;
public static class TypeLink extends Type
{
private final String linkedTypeName;
private final Range cardinality;
private Type linkedType;
public TypeLink( String linkedTypeName, Range cardinality )
{
this.linkedTypeName = linkedTypeName;
this.cardinality = cardinality;
}
protected Set< Entry< String, Type > > subTypeSet()
{
return linkedType.subTypeSet();
}
protected NativeType nativeType()
{
return linkedType.nativeType();
}
public String linkedTypeName()
{
return linkedTypeName;
}
public void setLinkedType( Type linkedType )
{
this.linkedType = linkedType;
}
public void cutChildrenFromValue( Value value )
{
linkedType.cutChildrenFromValue( value );
}
protected Range cardinality()
{
return cardinality;
}
protected void check( Value value, StringBuilder pathBuilder )
throws TypeCheckingException
{
linkedType.check( value, pathBuilder );
}
protected Value cast( Value value, StringBuilder pathBuilder )
throws TypeCastingException
{
return linkedType.cast( value, pathBuilder );
}
}
}