    001/*
    002 *  Licensed to the Apache Software Foundation (ASF) under one or more
    003 *  contributor license agreements.  See the NOTICE file distributed with
    004 *  this work for additional information regarding copyright ownership.
    005 *  The ASF licenses this file to You under the Apache License, Version 2.0
    006 *  (the "License"); you may not use this file except in compliance with
    007 *  the License.  You may obtain a copy of the License at
    008 *
    009 *     http://www.apache.org/licenses/LICENSE-2.0
    010 *
    011 *  Unless required by applicable law or agreed to in writing, software
    012 *  distributed under the License is distributed on an "AS IS" BASIS,
    013 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014 *  See the License for the specific language governing permissions and
    015 *  limitations under the License.
    016 */
    017package org.apache.commons.compress.harmony.unpack200;
    018
    019import org.apache.commons.compress.harmony.pack200.Codec;
    020import org.apache.commons.compress.harmony.pack200.Pack200Exception;
    021import org.apache.commons.compress.harmony.unpack200.bytecode.ClassFileEntry;
    022
    023/**
    024 * AttributeLayout defines a layout that describes how an attribute will be transmitted.
    025 */
    026public class AttributeLayout implements IMatcher {
    027
    028    public static final String ACC_ABSTRACT = "ACC_ABSTRACT"; //$NON-NLS-1$
    029    public static final String ACC_ANNOTATION = "ACC_ANNOTATION"; //$NON-NLS-1$
    030    public static final String ACC_ENUM = "ACC_ENUM"; //$NON-NLS-1$
    031    public static final String ACC_FINAL = "ACC_FINAL"; //$NON-NLS-1$
    032    public static final String ACC_INTERFACE = "ACC_INTERFACE"; //$NON-NLS-1$
    033    public static final String ACC_NATIVE = "ACC_NATIVE"; //$NON-NLS-1$
    034    public static final String ACC_PRIVATE = "ACC_PRIVATE"; //$NON-NLS-1$
    035    public static final String ACC_PROTECTED = "ACC_PROTECTED"; //$NON-NLS-1$
    036    public static final String ACC_PUBLIC = "ACC_PUBLIC"; //$NON-NLS-1$
    037    public static final String ACC_STATIC = "ACC_STATIC"; //$NON-NLS-1$
    038    public static final String ACC_STRICT = "ACC_STRICT"; //$NON-NLS-1$
    039    public static final String ACC_SYNCHRONIZED = "ACC_SYNCHRONIZED"; //$NON-NLS-1$
    040    public static final String ACC_SYNTHETIC = "ACC_SYNTHETIC"; //$NON-NLS-1$
    041    public static final String ACC_TRANSIENT = "ACC_TRANSIENT"; //$NON-NLS-1$
    042    public static final String ACC_VOLATILE = "ACC_VOLATILE"; //$NON-NLS-1$
    043    public static final String ATTRIBUTE_ANNOTATION_DEFAULT = "AnnotationDefault"; //$NON-NLS-1$
    044    public static final String ATTRIBUTE_CLASS_FILE_VERSION = "class-file version"; //$NON-NLS-1$
    045    public static final String ATTRIBUTE_CODE = "Code"; //$NON-NLS-1$
    046    public static final String ATTRIBUTE_CONSTANT_VALUE = "ConstantValue"; //$NON-NLS-1$
    047    public static final String ATTRIBUTE_DEPRECATED = "Deprecated"; //$NON-NLS-1$
    048    public static final String ATTRIBUTE_ENCLOSING_METHOD = "EnclosingMethod"; //$NON-NLS-1$
    049    public static final String ATTRIBUTE_EXCEPTIONS = "Exceptions"; //$NON-NLS-1$
    050    public static final String ATTRIBUTE_INNER_CLASSES = "InnerClasses"; //$NON-NLS-1$
    051    public static final String ATTRIBUTE_LINE_NUMBER_TABLE = "LineNumberTable"; //$NON-NLS-1$
    052    public static final String ATTRIBUTE_LOCAL_VARIABLE_TABLE = "LocalVariableTable"; //$NON-NLS-1$
    053    public static final String ATTRIBUTE_LOCAL_VARIABLE_TYPE_TABLE = "LocalVariableTypeTable"; //$NON-NLS-1$
    054    public static final String ATTRIBUTE_RUNTIME_INVISIBLE_ANNOTATIONS = "RuntimeInvisibleAnnotations"; //$NON-NLS-1$
    055    public static final String ATTRIBUTE_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS = "RuntimeInvisibleParameterAnnotations"; //$NON-NLS-1$
    056    public static final String ATTRIBUTE_RUNTIME_VISIBLE_ANNOTATIONS = "RuntimeVisibleAnnotations"; //$NON-NLS-1$
    057    public static final String ATTRIBUTE_RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS = "RuntimeVisibleParameterAnnotations"; //$NON-NLS-1$
    058    public static final String ATTRIBUTE_SIGNATURE = "Signature"; //$NON-NLS-1$
    059    public static final String ATTRIBUTE_SOURCE_FILE = "SourceFile"; //$NON-NLS-1$
    060    public static final int CONTEXT_CLASS = 0;
    061    public static final int CONTEXT_CODE = 3;
    062    public static final int CONTEXT_FIELD = 1;
    063    public static final int CONTEXT_METHOD = 2;
    064    public static final String[] contextNames = {"Class", "Field", "Method", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
    065        "Code",}; //$NON-NLS-1$
    066
    067    private static ClassFileEntry getValue(final String layout, long value, final SegmentConstantPool pool)
    068        throws Pack200Exception {
    069        if (layout.startsWith("R")) { //$NON-NLS-1$
    070            // references
    071            if (layout.indexOf('N') != -1) {
    072                value--;
    073            }
    074            if (layout.startsWith("RU")) { //$NON-NLS-1$
    075                return pool.getValue(SegmentConstantPool.UTF_8, value);
    076            }
    077            if (layout.startsWith("RS")) { //$NON-NLS-1$
    078                return pool.getValue(SegmentConstantPool.SIGNATURE, value);
    079            }
    080        } else if (layout.startsWith("K")) { //$NON-NLS-1$
    081            final char type = layout.charAt(1);
    082            switch (type) {
    083            case 'S': // String
    084                return pool.getValue(SegmentConstantPool.CP_STRING, value);
    085            case 'I': // Int (or byte or short)
    086            case 'C': // Char
    087                return pool.getValue(SegmentConstantPool.CP_INT, value);
    088            case 'F': // Float
    089                return pool.getValue(SegmentConstantPool.CP_FLOAT, value);
    090            case 'J': // Long
    091                return pool.getValue(SegmentConstantPool.CP_LONG, value);
    092            case 'D': // Double
    093                return pool.getValue(SegmentConstantPool.CP_DOUBLE, value);
    094            }
    095        }
    096        throw new Pack200Exception("Unknown layout encoding: " + layout);
    097    }
    098
    099    private final int context;
    100
    101    private final int index;
    102
    103    private final String layout;
    104
    105    private long mask;
    106
    107    private final String name;
    108    private final boolean isDefault;
    109    private int backwardsCallCount;
    110
    111    /**
    112     * Construct a default AttributeLayout (equivalent to
    113     * <code>new AttributeLayout(name, context, layout, index, true);</code>)
    114     *
    115     * @param name TODO
    116     * @param context TODO
    117     * @param layout TODO
    118     * @param index TODO
    119     * @throws Pack200Exception Attribute context out of range.
    120     * @throws Pack200Exception Cannot have a null layout.
    121     * @throws Pack200Exception Cannot have an unnamed layout.
    122     */
    123    public AttributeLayout(final String name, final int context, final String layout, final int index)
    124        throws Pack200Exception {
    125        this(name, context, layout, index, true);
    126    }
    127
    128    public AttributeLayout(final String name, final int context, final String layout, final int index,
    129        final boolean isDefault) throws Pack200Exception {
    130        super();
    131        this.index = index;
    132        this.context = context;
    133        if (index >= 0) {
    134            this.mask = 1L << index;
    135        } else {
    136            this.mask = 0;
    137        }
    138        if (context != CONTEXT_CLASS && context != CONTEXT_CODE && context != CONTEXT_FIELD
    139            && context != CONTEXT_METHOD) {
    140            throw new Pack200Exception("Attribute context out of range: " + context);
    141        }
    142        if (layout == null) {
    143            throw new Pack200Exception("Cannot have a null layout");
    144        }
    145        if (name == null || name.length() == 0) {
    146            throw new Pack200Exception("Cannot have an unnamed layout");
    147        }
    148        this.name = name;
    149        this.layout = layout;
    150        this.isDefault = isDefault;
    151    }
    152
    153    public Codec getCodec() {
    154        if (layout.indexOf('O') >= 0) {
    155            return Codec.BRANCH5;
    156        }
    157        if (layout.indexOf('P') >= 0) {
    158            return Codec.BCI5;
    159        }
    160        if (layout.indexOf('S') >= 0 && layout.indexOf("KS") < 0 //$NON-NLS-1$
    161            && layout.indexOf("RS") < 0) { //$NON-NLS-1$
    162            return Codec.SIGNED5;
    163        }
    164        if (layout.indexOf('B') >= 0) {
    165            return Codec.BYTE1;
    166        }
    167        return Codec.UNSIGNED5;
    168    }
    169
    170    public String getLayout() {
    171        return layout;
    172    }
    173
    174    public ClassFileEntry getValue(final long value, final SegmentConstantPool pool) throws Pack200Exception {
    175        return getValue(layout, value, pool);
    176    }
    177
    178    public ClassFileEntry getValue(final long value, final String type, final SegmentConstantPool pool)
    179        throws Pack200Exception {
    180        // TODO This really needs to be better tested, esp. the different types
    181        // TODO This should have the ability to deal with RUN stuff too, and
    182        // unions
    183        if (!layout.startsWith("KQ")) {
    184            return getValue(layout, value, pool);
    185        }
    186        if (type.equals("Ljava/lang/String;")) { //$NON-NLS-1$
    187            final ClassFileEntry value2 = getValue("KS", value, pool); //$NON-NLS-1$
    188            return value2;
    189        }
    190        return getValue("K" + type + layout.substring(2), value, //$NON-NLS-1$
    191            pool);
    192    }
    193
    194    @Override
    195    public int hashCode() {
    196        final int PRIME = 31;
    197        int r = 1;
    198        if (name != null) {
    199            r = r * PRIME + name.hashCode();
    200        }
    201        if (layout != null) {
    202            r = r * PRIME + layout.hashCode();
    203        }
    204        r = r * PRIME + index;
    205        r = r * PRIME + context;
    206        return r;
    207    }
    208
    209    /*
    210     * (non-Javadoc)
    211     *
    212     * @see org.apache.commons.compress.harmony.unpack200.IMatches#matches(long)
    213     */
    214    @Override
    215    public boolean matches(final long value) {
    216        return (value & mask) != 0;
    217    }
    218
    219    @Override
    220    public String toString() {
    221        return contextNames[context] + ": " + name;
    222    }
    223
    224    public int getContext() {
    225        return context;
    226    }
    227
    228    public int getIndex() {
    229        return index;
    230    }
    231
    232    public String getName() {
    233        return name;
    234    }
    235
    236    public int numBackwardsCallables() {
    237        if (layout == "*") {
    238            return 1;
    239        }
    240        return backwardsCallCount;
    241    }
    242
    243    public boolean isDefaultLayout() {
    244        return isDefault;
    245    }
    246
    247    public void setBackwardsCallCount(final int backwardsCallCount) {
    248        this.backwardsCallCount = backwardsCallCount;
    249    }
    250
    251}