blob: a5f0a2f716aaaf3dc72fe4a163f3bb3551e96cd2 [file] [log] [blame]
/*
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.flex.abc.semantics;
import java.util.HashMap;
import java.util.Vector;
import org.apache.flex.abc.ABCConstants;
import org.apache.flex.abc.semantics.Metadata;
import org.apache.flex.abc.semantics.Name;
import static org.apache.flex.abc.ABCConstants.*;
/**
* A representation of an individual <a href="http://learn.adobe.com/wiki/display/AVM2/4.8+Trait">trait</a>.
*/
public class Trait
{
public static final String TRAIT_CLASS = "class_id";
public static final String TRAIT_DISP = "disp_id";
public static final String TRAIT_FINAL = "final";
public static final String TRAIT_METHOD = "method_id";
public static final String TRAIT_NAME = "name";
public static final String TRAIT_OVERRIDE = "override";
public static final String TRAIT_PUBLIC = "public";
public static final String TRAIT_SLOT = "slot_id";
public static final String TRAIT_TYPE = "type";
public static final String SLOT_VALUE = "value";
/**
* Construct a Trait.
*
* @param kind - the Trait's kind nibble.
*/
public Trait(int kind, Name name)
{
this.kind_byte = (byte)(kind & 0x0F);
this.name = name;
addAttr(TRAIT_NAME, name);
}
/**
* This trait's kind byte. Flags are set in the high nibble of the kind
* byte, but only the lower nibble is stored here.
*
* @see #getFullKindByte()
*/
private byte kind_byte;
/**
* This trait's name. This is also denormalized into the trait's attributes
* as TRAIT_NAME.
*/
private final Name name;
/**
* Attributes of this trait. TODO: This shows its origins in abcasm, should
* be converted to a more type-safe system and an adapter layer added to
* abcasm to translate attribute names to their corresponding trait
* attributes.
*/
HashMap<String, Object> attrs = new HashMap<String, Object>();
/**
* Metadata attached to this trait.
*/
private Vector<Metadata> metaData;
/**
* Add a trait attribute.
*
* @param key - the attribute's name.
* @param value - the attribute's value.
* <pre> the attribute must not be present.</pre>
*/
public void addAttr(String key, Object value)
{
if (attrs.containsKey(key))
{
throw new IllegalArgumentException("Trait attribute " + key + " cannot be specified twice.");
}
attrs.put(key, value);
}
/**
* @return the low nibble of the kind byte (the actual trait kind).
*/
public byte getKind()
{
return (byte)(kind_byte & 0x0F);
}
/**
* @return the kind byte with its flags set in the high nibble.
*/
public byte getFullKindByte()
{
int result = getKind();
if (hasAttr(TRAIT_FINAL) && getBooleanAttr(TRAIT_FINAL))
result = result | (ABCConstants.ATTR_final << 4);
if (hasAttr(TRAIT_OVERRIDE) && getBooleanAttr(TRAIT_OVERRIDE))
result = result | (ABCConstants.ATTR_override << 4);
if (this.hasMetadata())
result = result | (TRAIT_FLAG_metadata << 4);
return (byte)result;
}
/**
* @return the Trait's name.
*/
public Name getName()
{
return this.name;
}
/**
* Set a trait attribute.
*
* @param attr_name - the attribute's name.
* @param attr_value - the attribute's value.
*/
public void setAttr(String attr_name, Object attr_value)
{
attrs.put(attr_name, attr_value);
}
/**
* Determine whether an attribute is present.
*
* @param attr_name - the attribute's name.
* @return true if the attribute is present.
*/
public boolean hasAttr(String attr_name)
{
return attrs.containsKey(attr_name);
}
/**
* Get an attribute's value.
*
* @param attr_name - the attribute's name.
* @return the value of the specified attribute.
* @pre the attribute must be present.
*/
public Object getAttr(String attr_name)
{
verifyContains(attr_name, null);
return attrs.get(attr_name);
}
/**
* Get a integer attribute's value.
*
* @param attr_name - the attribute's name.
* @return the value of the specified attribute.
* @pre the attribute must be present and must be of Integer type.
*/
public int getIntAttr(String attr_name)
{
verifyContains(attr_name, Integer.class);
return (Integer)attrs.get(attr_name);
}
/**
* Get a Name attribute's value.
*
* @param attr_name - the attribute's name.
* @return the value of the specified attribute.
* @pre the attribute must be present and must be of Name type.
*/
public Name getNameAttr(String attr_name)
{
verifyContains(attr_name, Name.class);
return (Name)attrs.get(attr_name);
}
/**
* Get a boolean attribute's value.
*
* @param attr_name - the attribute's name.
* @return the value of the specified attribute.
* @pre the attribute must be present and must be of Boolean type.
*/
public boolean getBooleanAttr(String attr_name)
{
verifyContains(attr_name, Boolean.class);
return (Boolean)attrs.get(attr_name);
}
/**
* Ensure an attribute is present and of the specified type.
*
* @param attr_name - the attribute's name.
* @param clazz - the required class.
*/
void verifyContains(String attr_name, Class<? extends Object> clazz)
{
if (!attrs.containsKey(attr_name))
throw new IllegalArgumentException("Required attribute " + attr_name + " not found.");
if (!(null == clazz || null == attrs.get(attr_name) || attrs.get(attr_name).getClass().equals(clazz)))
throw new IllegalArgumentException("Attribute " + attr_name + " must be type " + clazz.getSimpleName());
}
/**
* @param kind_byte the kind_byte to set
*/
public void setKind(int kind_byte)
{
this.kind_byte = (byte)(kind_byte & 0x0F);
}
/**
* @return true if the trait is final.
*/
public boolean isFinal()
{
return ((kind_byte >> 4) & TRAIT_FLAG_final) != 0;
}
/**
* @return true if the trait is an override.
*/
public boolean isOverride()
{
return ((kind_byte >> 4) & TRAIT_FLAG_override) != 0;
}
/**
* @return true if the trait has metadata.
*/
public boolean hasMetadata()
{
// getFullKindByte() ensures the TRAIT_metatdata flag is set.
return this.metaData != null;
}
/**
* @return true if the trait is constant.
*/
public boolean isConst()
{
return getKind() == TRAIT_Const || getKind() == TRAIT_Getter;
}
/**
* @return true if the trait is a class trait.
*/
public boolean isClass()
{
return getKind() == TRAIT_Class;
}
/**
* @return true if the trait is a method trait.
*/
public boolean isMethod()
{
return getKind() == TRAIT_Method;
}
/**
* @return true if the trait is a getter trait.
*/
public boolean isGetter()
{
return getKind() == TRAIT_Getter;
}
/**
* @return true if the trait is a setter trait.
*/
public boolean isSetter()
{
return getKind() == TRAIT_Setter;
}
/**
* @return true if the trait is some type of slot trait.
*/
public boolean isSlot()
{
int tk = getKind();
return tk == TRAIT_Var || tk == TRAIT_Const || tk == TRAIT_Class;
}
/**
* Add a metadata entry to this trait.
*
* @param md - the metadata to add.
*/
public void addMetadata(Metadata md)
{
if (null == this.metaData)
this.metaData = new Vector<Metadata>();
this.metaData.add(md);
}
/**
* Common entry for empty metadata.
*/
private static final Vector<Metadata> emptyMetadata = new Vector<Metadata>();
/**
* @return this trait's metadata.
*/
public final Vector<Metadata> getMetadata()
{
if (this.metaData != null)
return this.metaData;
else
return emptyMetadata;
}
}