/*
* Copyright 2005 Joe Walker
*
* 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.directwebremoting.extend;
import org.directwebremoting.ConversionException;
/**
* A simple struct to hold data about a single converted javascript variable.
* An inbound variable will have either a value or a fileValue but not both.
* If file is <code>true</code> fileValue will be populated, otherwise value
* will be populated.
* @author Joe Walker [joe at getahead dot ltd dot uk]
*/
public final class InboundVariable
{
/**
* Parsing ctor
* @param context How we lookup references
* @param key The name of the variable that this was transfered as
* @param type The type information from javascript
* @param value The javascript variable converted to a string
*/
public InboundVariable(InboundContext context, String key, String type, String value)
{
this(context, key, type, new FormField(value));
}
/**
* Parsing ctor
* @param context How we lookup references
* @param key The name of the variable that this was transfered as
* @param type The type information from javascript
* @param fileField The javascript variable converted to a FormField
*/
public InboundVariable(InboundContext context, String key, String type, FormField fileField)
{
this.context = context;
this.key = key;
this.type = type;
this.members = null;
this.formField = fileField;
}
/**
* Constructor for when we need something to represent null
* @param context How we lookup references
*/
public InboundVariable(InboundContext context)
{
this.context = context;
this.key = null;
this.type = ProtocolConstants.INBOUND_NULL;
this.formField = null;
this.members = null;
dereferenced = true;
}
/**
* Constructor for when we need to temporarily create an InboundVariable
* for sorting out varargs
* @param context How we lookup references
*/
public InboundVariable(InboundContext context, InboundVariable[] members)
{
this.context = context;
this.key = null;
this.type = ProtocolConstants.INBOUND_VARARGS;
this.formField = null;
this.members = members;
dereferenced = true;
}
/**
* Accessor of the context of the variable: the other related variables
*/
public InboundContext getContext()
{
return context;
}
/**
* Attempt to de-reference an inbound variable.
* We try de-referencing as soon as possible (why? there is a good reason
* for it, it fixes some bug, but I can't remember what right now) However
* the referenced variable may not exist yet, so the de-referencing may
* fail, requiring us to have another go later.
* @throws ConversionException If cross-references don't add up
*/
public void dereference() throws ConversionException
{
int depth = 0;
while (ProtocolConstants.TYPE_REFERENCE.equals(type))
{
InboundVariable cd = context.getInboundVariable(formField.getString());
if (cd == null)
{
throw new ConversionException(getClass(), "Found reference to variable named '" + formField.getString() + "', but no variable of that name could be found.");
}
type = cd.type;
formField = cd.getFormField();
key = cd.key;
depth++;
if (depth > 20)
{
throw new ConversionException(getClass(), "Max depth exceeded when dereferencing " + formField.getString());
}
}
dereferenced = true;
}
/**
* If we are using object parameters that have specified types then the
* {@link ConverterManager} will need to get to know what the required type
* is.
* @return The requested object type, or null if one was not specified
*/
public String getNamedObjectType()
{
if (type.startsWith("Object_"))
{
return type.substring("Object_".length());
}
else
{
return null;
}
}
/**
* Was this type null on the way in
* @return true if the javascript variable was null or undefined.
*/
public boolean isNull()
{
return type.equals(ProtocolConstants.INBOUND_NULL);
}
/**
* @return Returns the value.
*/
public String getValue()
{
return formField.getString();
}
/**
* @return Returns the file value
*/
public FormField getFormField()
{
return formField;
}
/**
* Nasty hack to get around varargs
*/
public InboundVariable[] getMembers()
{
return members;
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString()
{
if (formField == null)
{
return type + ProtocolConstants.INBOUND_TYPE_SEPARATOR + "null";
}
return type + ProtocolConstants.INBOUND_TYPE_SEPARATOR + formField.toString();
}
/* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj)
{
if (this == obj)
{
return true;
}
if (!(obj instanceof InboundVariable))
{
return false;
}
InboundVariable that = (InboundVariable) obj;
if (!dereferenced)
{
// We shouldn't really need to do this ...
// dereference();
throw new IllegalStateException("this.equals() called before dereference()");
}
if (!that.dereferenced)
{
// that.dereference();
throw new IllegalStateException("that.equals() called before dereference()");
}
if (!this.type.equals(that.type))
{
return false;
}
if (!this.formField.equals(that.formField))
{
return false;
}
if (this.key == null || that.key == null)
{
return false;
}
return true; // this.key.equals(that.key);
}
/* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode()
{
if (!dereferenced)
{
// dereference();
throw new IllegalStateException("hashCode() called before dereference()");
}
int hash = 745;
hash += (formField == null) ? 875 : formField.hashCode();
hash += (type == null) ? 346 : type.hashCode();
hash += (key == null) ? 768 : key.hashCode();
return hash;
}
/**
* Nasty hack to get around varargs
*/
private final InboundVariable[] members;
/**
* It's an error to store this in a Map unless we have already called
* {@link #dereference()}.
*/
private boolean dereferenced = false;
/**
* How do be lookup references?
*/
private final InboundContext context;
/**
* The variable name
*/
private String key;
/**
* The javascript declared variable type
*/
private String type;
/**
* The javascript declared file value
*/
private FormField formField;
}