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        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            return getValue("KS", value, pool);
188        }
189        return getValue("K" + type + layout.substring(2), value, //$NON-NLS-1$
190            pool);
191    }
192
193    @Override
194    public int hashCode() {
195        final int PRIME = 31;
196        int r = 1;
197        if (name != null) {
198            r = r * PRIME + name.hashCode();
199        }
200        if (layout != null) {
201            r = r * PRIME + layout.hashCode();
202        }
203        r = r * PRIME + index;
204        r = r * PRIME + context;
205        return r;
206    }
207
208    /*
209     * (non-Javadoc)
210     *
211     * @see org.apache.commons.compress.harmony.unpack200.IMatches#matches(long)
212     */
213    @Override
214    public boolean matches(final long value) {
215        return (value & mask) != 0;
216    }
217
218    @Override
219    public String toString() {
220        return contextNames[context] + ": " + name;
221    }
222
223    public int getContext() {
224        return context;
225    }
226
227    public int getIndex() {
228        return index;
229    }
230
231    public String getName() {
232        return name;
233    }
234
235    public int numBackwardsCallables() {
236        if (layout == "*") {
237            return 1;
238        }
239        return backwardsCallCount;
240    }
241
242    public boolean isDefaultLayout() {
243        return isDefault;
244    }
245
246    public void setBackwardsCallCount(final int backwardsCallCount) {
247        this.backwardsCallCount = backwardsCallCount;
248    }
249
250}