/* * * 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; } }