/***************************************************************************
* Copyright (C) by Fabrizio Montesi *
* *
* 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;
import jolie.runtime.expression.Expression;
import jolie.ExecutionThread;
import jolie.process.TransformationReason;
import jolie.util.Pair;
/**
* Represents a variable path, e.g. a.b[3], offering mechanisms
* for referring to the object pointed by it.
* @author Fabrizio Montesi
*/
public class VariablePath implements Expression, Cloneable
{
public static class EmptyPathLazyHolder {
private EmptyPathLazyHolder() {}
public static final Pair< Expression, Expression >[] emptyPath = new Pair[0];
}
private final Pair< Expression, Expression >[] path; // Right Expression may be null
protected final Pair< Expression, Expression >[] path()
{
return path;
}
public boolean isGlobal()
{
return false;
}
protected static Pair< Expression, Expression >[] cloneExpressionHelper( Pair< Expression, Expression >[] path, TransformationReason reason )
{
Pair< Expression, Expression >[] clonedPath = new Pair[ path.length ];
for( int i = 0; i < path.length; i++ ) {
clonedPath[i] = new Pair< Expression, Expression >(
path[i].key().cloneExpression( reason ),
( path[i].value() == null ) ? null : path[i].value().cloneExpression( reason )
);
}
return clonedPath;
}
@Override
public VariablePath clone()
{
return new VariablePath( path );
}
public Expression cloneExpression( TransformationReason reason )
{
Pair< Expression, Expression >[] clonedPath = cloneExpressionHelper( path, reason );
return new VariablePath( clonedPath );
}
public final VariablePath containedSubPath( VariablePath otherVarPath )
{
if ( getRootValue() != otherVarPath.getRootValue() )
return null;
// If the other path is shorter than this, it's not a subpath.
if ( otherVarPath.path.length < path.length )
return null;
int i, myIndex, otherIndex;
Pair< Expression, Expression > pair, otherPair;
Expression expr, otherExpr;
for( i = 0; i < path.length; i++ ) {
pair = path[i];
otherPair = otherVarPath.path[i];
// *.element_name is not a subpath of *.other_name
if ( !pair.key().evaluate().strValue().equals( otherPair.key().evaluate().strValue() ) )
return null;
// If element name is equal, check for the same index
expr = pair.value();
otherExpr = otherPair.value();
myIndex = ( expr == null ) ? 0 : expr.evaluate().intValue();
otherIndex = ( otherExpr == null ) ? 0 : otherExpr.evaluate().intValue();
if ( myIndex != otherIndex )
return null;
}
// Now i represents the beginning of the subpath, we can just copy it from there
Pair< Expression, Expression >[] subPath = new Pair[ otherVarPath.path.length - i ];
System.arraycopy( otherVarPath.path, i, subPath, 0, otherVarPath.path.length - i );
/*for( int k = 0; i < otherVarPath.path.length; i++ ) {
subPath[k] = otherVarPath.path[i];
k++;
}*/
return _createVariablePath( subPath );
}
protected VariablePath _createVariablePath( Pair< Expression, Expression >[] path )
{
return new VariablePath( path );
}
public VariablePath( Pair< Expression, Expression >[] path )
{
this.path = path;
}
protected Value getRootValue()
{
return ExecutionThread.currentThread().state().root();
}
public final void undef()
{
Pair< Expression, Expression > pair = null;
ValueVector currVector = null;
Value currValue = getRootValue();
int index;
String keyStr;
for( int i = 0; i < path.length; i++ ) {
pair = path[i];
keyStr = pair.key().evaluate().strValue();
currVector = currValue.children().get( keyStr );
if ( currVector == null ) {
return;
} else if ( currVector.size() < 1 ) {
if ( currVector.isLink() ) {
currValue.children().remove( keyStr );
}
return;
}
if ( pair.value() == null ) {
if ( (i+1) < path.length ) {
currValue = currVector.get( 0 );
} else { // We're finished
currValue.children().remove( keyStr );
}
} else {
index = pair.value().evaluate().intValue();
if ( (i+1) < path.length ) {
if ( currVector.size() <= index ) {
return;
}
currValue = currVector.get( index );
} else {
if ( currVector.size() > index ) {
currVector.remove( index );
}
}
}
}
}
public final Value getValue()
{
return getValue( getRootValue() );
}
public final Value getValue( Value rootValue )
{
Value currValue = rootValue;
String keyStr;
for( Pair< Expression, Expression > pair : path ) {
keyStr = pair.key().evaluate().strValue();
if ( pair.value() == null )
currValue =
currValue.getFirstChild( keyStr );
else
currValue =
currValue.getChildren( keyStr ).get( pair.value().evaluate().intValue() );
}
return currValue;
}
public final void setValue( Value value )
{
Pair< Expression, Expression > pair = null;
ValueVector currVector = null;
Value currValue = getRootValue();
int index;
String keyStr;
for( int i = 0; i < path.length; i++ ) {
pair = path[i];
keyStr = pair.key().evaluate().strValue();
currVector = currValue.getChildren( keyStr );
if ( pair.value() == null ) {
if ( (i+1) < path.length ) {
currValue = currVector.get( 0 );
} else { // We're finished
if ( currVector.get( 0 ).isUsedInCorrelation() ) {
currVector.get( 0 ).refCopy( value );
} else {
currVector.set( 0, value );
}
}
} else {
index = pair.value().evaluate().intValue();
if ( (i+1) < path.length ) {
currValue = currVector.get( index );
} else {
if ( currVector.get( index ).isUsedInCorrelation() ) {
currVector.get( index ).refCopy( value );
} else {
currVector.set( index, value );
}
}
}
}
}
public final Value getValueOrNull()
{
return getValueOrNull( getRootValue() );
}
public final Value getValueOrNull( Value rootValue )
{
Pair< Expression, Expression > pair = null;
ValueVector currVector = null;
Value currValue = rootValue;
int index;
for( int i = 0; i < path.length; i++ ) {
pair = path[i];
currVector = currValue.children().get( pair.key().evaluate().strValue() );
if ( currVector == null ) {
return null;
}
if ( pair.value() == null ) {
if ( (i+1) < path.length ) {
if ( currVector.isEmpty() ) {
return null;
}
currValue = currVector.get( 0 );
} else { // We're finished
if ( currVector.isEmpty() ) {
return null;
} else {
return currVector.get( 0 );
}
}
} else {
index = pair.value().evaluate().intValue();
if ( currVector.size() <= index ) {
return null;
}
currValue = currVector.get( index );
if ( (i+1) >= path.length ) {
return currValue;
}
}
}
return currValue;
}
public final ValueVector getValueVector( Value rootValue )
{
Pair< Expression, Expression > pair;
ValueVector currVector = null;
Value currValue = rootValue;
for( int i = 0; i < path.length; i++ ) {
pair = path[i];
currVector = currValue.getChildren( pair.key().evaluate().strValue() );
if ( (i+1) < path.length ) {
if ( pair.value() == null ) {
currValue = currVector.get( 0 );
} else {
currValue = currVector.get( pair.value().evaluate().intValue() );
}
}
}
return currVector;
}
public final ValueVector getValueVector()
{
return getValueVector( getRootValue() );
}
public final void makePointer( VariablePath rightPath )
{
Pair< Expression, Expression > pair;
ValueVector currVector;
Value currValue = getRootValue();
int index;
String keyStr;
for( int i = 0; i < path.length; i++ ) {
pair = path[i];
keyStr = pair.key().evaluate().strValue();
currVector = currValue.getChildren( keyStr );
if ( pair.value() == null ) {
if ( (i+1) < path.length ) {
currValue = currVector.get( 0 );
} else { // We're finished
currValue.children().put( keyStr, ValueVector.createLink( rightPath ) );
}
} else {
index = pair.value().evaluate().intValue();
if ( (i+1) < path.length ) {
currValue = currVector.get( index );
} else {
currVector.set( index, Value.createLink( rightPath ) );
}
}
}
}
private Object getValueOrValueVector()
{
Pair< Expression, Expression > pair;
ValueVector currVector;
Value currValue = getRootValue();
int index;
for( int i = 0; i < path.length; i++ ) {
pair = path[i];
currVector = currValue.getChildren( pair.key().evaluate().strValue() );
if ( pair.value() == null ) {
if ( (i+1) < path.length ) {
currValue = currVector.get( 0 );
} else { // We're finished
return currVector;
}
} else {
index = pair.value().evaluate().intValue();
if ( (i+1) < path.length ) {
currValue = currVector.get( index );
} else {
return currVector.get( index );
}
}
}
return currValue;
}
@SuppressWarnings("unchecked")
public final void deepCopy( VariablePath rightPath )
{
Object myObj = getValueOrValueVector();
if ( myObj instanceof Value ) {
((Value) myObj).deepCopy( rightPath.getValue() );
} else {
ValueVector myVec = (ValueVector) myObj;
ValueVector rightVec = rightPath.getValueVector();
for( int i = 0; i < rightVec.size(); i++ ) {
myVec.get( i ).deepCopy( rightVec.get( i ) );
}
}
}
public final Value evaluate()
{
final Value v = getValueOrNull();
if ( v == null )
return Value.UNDEFINED_VALUE;
return v;
}
}