    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);})
    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        this.index = index;
    131        this.context = context;
    132        if (index >= 0) {
    133            this.mask = 1L << index;
    134        } else {
    135            this.mask = 0;
    136        }
    137        if (context != CONTEXT_CLASS && context != CONTEXT_CODE && context != CONTEXT_FIELD
    138            && context != CONTEXT_METHOD) {
    139            throw new Pack200Exception("Attribute context out of range: " + context);
    140        }
    141        if (layout == null) {
    142            throw new Pack200Exception("Cannot have a null layout");
    143        }
    144        if (name == null || name.length() == 0) {
    145            throw new Pack200Exception("Cannot have an unnamed layout");
    146        }
    147        this.name = name;
    148        this.layout = layout;
    149        this.isDefault = isDefault;
    150    }
    151
    152    public Codec getCodec() {
    153        if (layout.indexOf('O') >= 0) {
    154            return Codec.BRANCH5;
    155        }
    156        if (layout.indexOf('P') >= 0) {
    157            return Codec.BCI5;
    158        }
    159        if (layout.indexOf('S') >= 0 && layout.indexOf("KS") < 0 //$NON-NLS-1$
    160            && layout.indexOf("RS") < 0) { //$NON-NLS-1$
    161            return Codec.SIGNED5;
    162        }
    163        if (layout.indexOf('B') >= 0) {
    164            return Codec.BYTE1;
    165        }
    166        return Codec.UNSIGNED5;
    167    }
    168
    169    public int getContext() {
    170        return context;
    171    }
    172
    173    public int getIndex() {
    174        return index;
    175    }
    176
    177    public String getLayout() {
    178        return layout;
    179    }
    180
    181    public String getName() {
    182        return name;
    183    }
    184
    185    public ClassFileEntry getValue(final long value, final SegmentConstantPool pool) throws Pack200Exception {
    186        return getValue(layout, value, pool);
    187    }
    188
    189    public ClassFileEntry getValue(final long value, final String type, final SegmentConstantPool pool)
    190        throws Pack200Exception {
    191        // TODO This really needs to be better tested, esp. the different types
    192        // TODO This should have the ability to deal with RUN stuff too, and
    193        // unions
    194        if (!layout.startsWith("KQ")) {
    195            return getValue(layout, value, pool);
    196        }
    197        if (type.equals("Ljava/lang/String;")) { //$NON-NLS-1$
    198            return getValue("KS", value, pool);
    199        }
    200        return getValue("K" + type + layout.substring(2), value, //$NON-NLS-1$
    201            pool);
    202    }
    203
    204    @Override
    205    public int hashCode() {
    206        final int PRIME = 31;
    207        int r = 1;
    208        if (name != null) {
    209            r = r * PRIME + name.hashCode();
    210        }
    211        if (layout != null) {
    212            r = r * PRIME + layout.hashCode();
    213        }
    214        r = r * PRIME + index;
    215        r = r * PRIME + context;
    216        return r;
    217    }
    218
    219    public boolean isDefaultLayout() {
    220        return isDefault;
    221    }
    222
    223    /*
    224     * (non-Javadoc)
    225     *
    226     * @see org.apache.commons.compress.harmony.unpack200.IMatches#matches(long)
    227     */
    228    @Override
    229    public boolean matches(final long value) {
    230        return (value & mask) != 0;
    231    }
    232
    233    public int numBackwardsCallables() {
    234        if (layout == "*") {
    235            return 1;
    236        }
    237        return backwardsCallCount;
    238    }
    239
    240    public void setBackwardsCallCount(final int backwardsCallCount) {
    241        this.backwardsCallCount = backwardsCallCount;
    242    }
    243
    244    @Override
    245    public String toString() {
    246        return contextNames[context] + ": " + name;
    247    }
    248
    249}