    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one
    003 * or more contributor license agreements.  See the NOTICE file
    004 * distributed with this work for additional information
    005 * regarding copyright ownership.  The ASF licenses this file
    006 * to you under the Apache License, Version 2.0 (the
    007 * "License"); you may not use this file except in compliance
    008 * with the License.  You may obtain a copy of the License at
    009 *
    010 *   https://www.apache.org/licenses/LICENSE-2.0
    011 *
    012 * Unless required by applicable law or agreed to in writing,
    013 * software distributed under the License is distributed on an
    014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    015 * KIND, either express or implied.  See the License for the
    016 * specific language governing permissions and limitations
    017 * under the License.
    018 */
    019package org.apache.commons.compress.harmony.unpack200;
    020
    021import org.apache.commons.compress.harmony.pack200.Codec;
    022import org.apache.commons.compress.harmony.pack200.Pack200Exception;
    023import org.apache.commons.compress.harmony.unpack200.bytecode.ClassFileEntry;
    024import org.apache.commons.lang3.StringUtils;
    025
    026/**
    027 * Defines a layout that describes how an attribute will be transmitted.
    028 */
    029public class AttributeLayout implements IMatcher {
    030
    031    /**
    032     * {@value}
    033     */
    034    public static final String ACC_ABSTRACT = "ACC_ABSTRACT"; //$NON-NLS-1$
    035
    036    /**
    037     * {@value}
    038     */
    039    public static final String ACC_ANNOTATION = "ACC_ANNOTATION"; //$NON-NLS-1$
    040
    041    /**
    042     * {@value}
    043     */
    044    public static final String ACC_ENUM = "ACC_ENUM"; //$NON-NLS-1$
    045
    046    /**
    047     * {@value}
    048     */
    049    public static final String ACC_FINAL = "ACC_FINAL"; //$NON-NLS-1$
    050
    051    /**
    052     * {@value}
    053     */
    054    public static final String ACC_INTERFACE = "ACC_INTERFACE"; //$NON-NLS-1$
    055
    056    /**
    057     * {@value}
    058     */
    059    public static final String ACC_NATIVE = "ACC_NATIVE"; //$NON-NLS-1$
    060
    061    /**
    062     * {@value}
    063     */
    064    public static final String ACC_PRIVATE = "ACC_PRIVATE"; //$NON-NLS-1$
    065
    066    /**
    067     * {@value}
    068     */
    069    public static final String ACC_PROTECTED = "ACC_PROTECTED"; //$NON-NLS-1$
    070
    071    /**
    072     * {@value}
    073     */
    074    public static final String ACC_PUBLIC = "ACC_PUBLIC"; //$NON-NLS-1$
    075
    076    /**
    077     * {@value}
    078     */
    079    public static final String ACC_STATIC = "ACC_STATIC"; //$NON-NLS-1$
    080
    081    /**
    082     * {@value}
    083     */
    084    public static final String ACC_STRICT = "ACC_STRICT"; //$NON-NLS-1$
    085
    086    /**
    087     * {@value}
    088     */
    089    public static final String ACC_SYNCHRONIZED = "ACC_SYNCHRONIZED"; //$NON-NLS-1$
    090
    091    /**
    092     * {@value}
    093     */
    094    public static final String ACC_SYNTHETIC = "ACC_SYNTHETIC"; //$NON-NLS-1$
    095
    096    /**
    097     * {@value}
    098     */
    099    public static final String ACC_TRANSIENT = "ACC_TRANSIENT"; //$NON-NLS-1$
    100
    101    /**
    102     * {@value}
    103     */
    104    public static final String ACC_VOLATILE = "ACC_VOLATILE"; //$NON-NLS-1$
    105
    106    /**
    107     * {@value}
    108     */
    109    public static final String ATTRIBUTE_ANNOTATION_DEFAULT = "AnnotationDefault"; //$NON-NLS-1$
    110
    111    /**
    112     * {@value}
    113     */
    114    public static final String ATTRIBUTE_CLASS_FILE_VERSION = "class-file version"; //$NON-NLS-1$
    115
    116    /**
    117     * {@value}
    118     */
    119    public static final String ATTRIBUTE_CODE = "Code"; //$NON-NLS-1$
    120
    121    /**
    122     * {@value}
    123     */
    124    public static final String ATTRIBUTE_CONSTANT_VALUE = "ConstantValue"; //$NON-NLS-1$
    125
    126    /**
    127     * {@value}
    128     */
    129    public static final String ATTRIBUTE_DEPRECATED = "Deprecated"; //$NON-NLS-1$
    130
    131    /**
    132     * {@value}
    133     */
    134    public static final String ATTRIBUTE_ENCLOSING_METHOD = "EnclosingMethod"; //$NON-NLS-1$
    135
    136    /**
    137     * {@value}
    138     */
    139    public static final String ATTRIBUTE_EXCEPTIONS = "Exceptions"; //$NON-NLS-1$
    140
    141    /**
    142     * {@value}
    143     */
    144    public static final String ATTRIBUTE_INNER_CLASSES = "InnerClasses"; //$NON-NLS-1$
    145
    146    /**
    147     * {@value}
    148     */
    149    public static final String ATTRIBUTE_LINE_NUMBER_TABLE = "LineNumberTable"; //$NON-NLS-1$
    150
    151    /**
    152     * {@value}
    153     */
    154    public static final String ATTRIBUTE_LOCAL_VARIABLE_TABLE = "LocalVariableTable"; //$NON-NLS-1$
    155
    156    /**
    157     * {@value}
    158     */
    159    public static final String ATTRIBUTE_LOCAL_VARIABLE_TYPE_TABLE = "LocalVariableTypeTable"; //$NON-NLS-1$
    160
    161    /**
    162     * {@value}
    163     */
    164    public static final String ATTRIBUTE_RUNTIME_INVISIBLE_ANNOTATIONS = "RuntimeInvisibleAnnotations"; //$NON-NLS-1$
    165
    166    /**
    167     * {@value}
    168     */
    169    public static final String ATTRIBUTE_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS = "RuntimeInvisibleParameterAnnotations"; //$NON-NLS-1$
    170
    171    /**
    172     * {@value}
    173     */
    174    public static final String ATTRIBUTE_RUNTIME_VISIBLE_ANNOTATIONS = "RuntimeVisibleAnnotations"; //$NON-NLS-1$
    175
    176    /**
    177     * {@value}
    178     */
    179    public static final String ATTRIBUTE_RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS = "RuntimeVisibleParameterAnnotations"; //$NON-NLS-1$
    180
    181    /**
    182     * {@value}
    183     */
    184    public static final String ATTRIBUTE_SIGNATURE = "Signature"; //$NON-NLS-1$
    185
    186    /**
    187     * {@value}
    188     */
    189    public static final String ATTRIBUTE_SOURCE_FILE = "SourceFile"; //$NON-NLS-1$
    190
    191    /**
    192     * {@value}
    193     */
    194    public static final int CONTEXT_CLASS = 0;
    195
    196    /**
    197     * {@value}
    198     */
    199    public static final int CONTEXT_CODE = 3;
    200
    201    /**
    202     * {@value}
    203     */
    204    public static final int CONTEXT_FIELD = 1;
    205
    206    /**
    207     * {@value}
    208     */
    209    public static final int CONTEXT_METHOD = 2;
    210
    211    /**
    212     * Context names.
    213     */
    214    public static final String[] contextNames = { "Class", "Field", "Method", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
    215            "Code", }; //$NON-NLS-1$
    216
    217    private static ClassFileEntry getValue(final String layout, long longIndex, final SegmentConstantPool pool) throws Pack200Exception {
    218        if (layout.startsWith("R")) { //$NON-NLS-1$
    219            // references
    220            if (layout.indexOf('N') != -1) {
    221                longIndex--;
    222            }
    223            if (layout.startsWith("RU")) { //$NON-NLS-1$
    224                return pool.getValue(SegmentConstantPool.UTF_8, longIndex);
    225            }
    226            if (layout.startsWith("RS")) { //$NON-NLS-1$
    227                return pool.getValue(SegmentConstantPool.SIGNATURE, longIndex);
    228            }
    229        } else if (layout.startsWith("K")) { //$NON-NLS-1$
    230            final char type = layout.charAt(1);
    231            switch (type) {
    232            case 'S': // String
    233                return pool.getValue(SegmentConstantPool.CP_STRING, longIndex);
    234            case 'I': // Int (or byte or short)
    235            case 'C': // Char
    236                return pool.getValue(SegmentConstantPool.CP_INT, longIndex);
    237            case 'F': // Float
    238                return pool.getValue(SegmentConstantPool.CP_FLOAT, longIndex);
    239            case 'J': // Long
    240                return pool.getValue(SegmentConstantPool.CP_LONG, longIndex);
    241            case 'D': // Double
    242                return pool.getValue(SegmentConstantPool.CP_DOUBLE, longIndex);
    243            }
    244        }
    245        throw new Pack200Exception("Unknown layout encoding: " + layout);
    246    }
    247
    248    private final int context;
    249
    250    private final int index;
    251
    252    private final String layout;
    253
    254    private long mask;
    255
    256    private final String name;
    257    private final boolean isDefault;
    258    private int backwardsCallCount;
    259
    260    /**
    261     * Constructs a default AttributeLayout (equivalent to {@code new AttributeLayout(name, context, layout, index, true);})
    262     *
    263     * @param name    The layout name.
    264     * @param context One of {@link #CONTEXT_CLASS}, {@link #CONTEXT_CODE}, {@link #CONTEXT_FIELD}, {@link #CONTEXT_METHOD}.
    265     * @param layout  The layout.
    266     * @param index   The index, currently used as part of computing the hash code.
    267     * @throws Pack200Exception Attribute context out of range.
    268     * @throws Pack200Exception Cannot have a null layout.
    269     * @throws Pack200Exception Cannot have an unnamed layout.
    270     */
    271    public AttributeLayout(final String name, final int context, final String layout, final int index) throws Pack200Exception {
    272        this(name, context, layout, index, true);
    273    }
    274
    275    /**
    276     * Constructs a default AttributeLayout (equivalent to {@code new AttributeLayout(name, context, layout, index, true);})
    277     *
    278     * @param name    The layout name.
    279     * @param context One of {@link #CONTEXT_CLASS}, {@link #CONTEXT_CODE}, {@link #CONTEXT_FIELD}, {@link #CONTEXT_METHOD}.
    280     * @param layout  The layout.
    281     * @param index   The index, currently used as part of computing the hash code.
    282     * @param isDefault Whether this is the default layout.
    283     * @throws Pack200Exception Attribute context out of range.
    284     * @throws Pack200Exception Cannot have a null layout.
    285     * @throws Pack200Exception Cannot have an unnamed layout.
    286     */
    287    public AttributeLayout(final String name, final int context, final String layout, final int index, final boolean isDefault) throws Pack200Exception {
    288        this.index = index;
    289        this.context = context;
    290        if (index >= 0) {
    291            this.mask = 1L << index;
    292        } else {
    293            this.mask = 0;
    294        }
    295        if (context != CONTEXT_CLASS && context != CONTEXT_CODE && context != CONTEXT_FIELD && context != CONTEXT_METHOD) {
    296            throw new Pack200Exception("Attribute context out of range: " + context);
    297        }
    298        if (layout == null) {
    299            throw new Pack200Exception("Cannot have a null layout");
    300        }
    301        if (StringUtils.isEmpty(name)) {
    302            throw new Pack200Exception("Cannot have an unnamed layout");
    303        }
    304        this.name = name;
    305        this.layout = layout;
    306        this.isDefault = isDefault;
    307    }
    308
    309    /**
    310     * Gets the Codec based on the layout.
    311     *
    312     * @return the Codec.
    313     */
    314    public Codec getCodec() {
    315        if (layout.indexOf('O') >= 0) {
    316            return Codec.BRANCH5;
    317        }
    318        if (layout.indexOf('P') >= 0) {
    319            return Codec.BCI5;
    320        }
    321        if (layout.indexOf('S') >= 0 && !layout.contains("KS") //$NON-NLS-1$
    322                && !layout.contains("RS")) { //$NON-NLS-1$
    323            return Codec.SIGNED5;
    324        }
    325        if (layout.indexOf('B') >= 0) {
    326            return Codec.BYTE1;
    327        }
    328        return Codec.UNSIGNED5;
    329    }
    330
    331    /**
    332     * Gets the context.
    333     *
    334     * @return the context.
    335     */
    336    public int getContext() {
    337        return context;
    338    }
    339
    340    /**
    341     * Gets the index.
    342     *
    343     * @return the index.
    344     */
    345    public int getIndex() {
    346        return index;
    347    }
    348
    349    /**
    350     * Gets the layout.
    351     *
    352     * @return the layout.
    353     */
    354    public String getLayout() {
    355        return layout;
    356    }
    357
    358    /**
    359     * Gets the name.
    360     *
    361     * @return the name.
    362     */
    363    public String getName() {
    364        return name;
    365    }
    366
    367    /**
    368     * Gets the ClassFileEntry for the given input.
    369     *
    370     * @param longIndex An index into the segment constant pool.
    371     * @param pool the segment constant pool.
    372     * @return the matching ClassFileEntry.
    373     * @throws Pack200Exception if the input is invalid.
    374     */
    375    public ClassFileEntry getValue(final long longIndex, final SegmentConstantPool pool) throws Pack200Exception {
    376        return getValue(layout, longIndex, pool);
    377    }
    378
    379    /**
    380     * Gets the ClassFileEntry for the given input.
    381     *
    382     * @param longIndex An index into the segment constant pool.
    383     * @param type the Java type signature.
    384     * @param pool the segment constant pool.
    385     * @return the matching ClassFileEntry.
    386     * @throws Pack200Exception if the input is invalid.
    387     */
    388    public ClassFileEntry getValue(final long longIndex, final String type, final SegmentConstantPool pool) throws Pack200Exception {
    389        // TODO This really needs to be better tested, esp. the different types
    390        // TODO This should have the ability to deal with RUN stuff too, and
    391        // unions
    392        if (!layout.startsWith("KQ")) {
    393            return getValue(layout, longIndex, pool);
    394        }
    395        if (type.equals("Ljava/lang/String;")) { //$NON-NLS-1$
    396            return getValue("KS", longIndex, pool);
    397        }
    398        return getValue("K" + type + layout.substring(2), longIndex, //$NON-NLS-1$
    399                pool);
    400    }
    401
    402    @Override
    403    public int hashCode() {
    404        final int prime = 31;
    405        int r = 1;
    406        if (name != null) {
    407            r = r * prime + name.hashCode();
    408        }
    409        if (layout != null) {
    410            r = r * prime + layout.hashCode();
    411        }
    412        r = r * prime + index;
    413        r = r * prime + context;
    414        return r;
    415    }
    416
    417    /**
    418     * Tests whether this is the default layout.
    419     *
    420     * @return whether this is the default layout.
    421     */
    422    public boolean isDefaultLayout() {
    423        return isDefault;
    424    }
    425
    426    /*
    427     * (non-Javadoc)
    428     *
    429     * @see org.apache.commons.compress.harmony.unpack200.IMatches#matches(long)
    430     */
    431    @Override
    432    public boolean matches(final long value) {
    433        return (value & mask) != 0;
    434    }
    435
    436    /**
    437     * Gets the backward call count.
    438     *
    439     * @return the backward call count.
    440     */
    441    public int numBackwardsCallables() {
    442        if ("*".equals(layout)) {
    443            return 1;
    444        }
    445        return backwardsCallCount;
    446    }
    447
    448    /**
    449     * Sets the backward call count.
    450     *
    451     * @param backwardsCallCount the backward call count.
    452     */
    453    public void setBackwardsCallCount(final int backwardsCallCount) {
    454        this.backwardsCallCount = backwardsCallCount;
    455    }
    456
    457    @Override
    458    public String toString() {
    459        return contextNames[context] + ": " + name;
    460    }
    461
    462}