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    /**
029     * {@value}
030     */
031    public static final String ACC_ABSTRACT = "ACC_ABSTRACT"; //$NON-NLS-1$
032
033    /**
034     * {@value}
035     */
036    public static final String ACC_ANNOTATION = "ACC_ANNOTATION"; //$NON-NLS-1$
037
038    /**
039     * {@value}
040     */
041    public static final String ACC_ENUM = "ACC_ENUM"; //$NON-NLS-1$
042
043    /**
044     * {@value}
045     */
046    public static final String ACC_FINAL = "ACC_FINAL"; //$NON-NLS-1$
047
048    /**
049     * {@value}
050     */
051    public static final String ACC_INTERFACE = "ACC_INTERFACE"; //$NON-NLS-1$
052
053    /**
054     * {@value}
055     */
056    public static final String ACC_NATIVE = "ACC_NATIVE"; //$NON-NLS-1$
057
058    /**
059     * {@value}
060     */
061    public static final String ACC_PRIVATE = "ACC_PRIVATE"; //$NON-NLS-1$
062
063    /**
064     * {@value}
065     */
066    public static final String ACC_PROTECTED = "ACC_PROTECTED"; //$NON-NLS-1$
067
068    /**
069     * {@value}
070     */
071    public static final String ACC_PUBLIC = "ACC_PUBLIC"; //$NON-NLS-1$
072
073    /**
074     * {@value}
075     */
076    public static final String ACC_STATIC = "ACC_STATIC"; //$NON-NLS-1$
077
078    /**
079     * {@value}
080     */
081    public static final String ACC_STRICT = "ACC_STRICT"; //$NON-NLS-1$
082
083    /**
084     * {@value}
085     */
086    public static final String ACC_SYNCHRONIZED = "ACC_SYNCHRONIZED"; //$NON-NLS-1$
087
088    /**
089     * {@value}
090     */
091    public static final String ACC_SYNTHETIC = "ACC_SYNTHETIC"; //$NON-NLS-1$
092
093    /**
094     * {@value}
095     */
096    public static final String ACC_TRANSIENT = "ACC_TRANSIENT"; //$NON-NLS-1$
097
098    /**
099     * {@value}
100     */
101    public static final String ACC_VOLATILE = "ACC_VOLATILE"; //$NON-NLS-1$
102
103    /**
104     * {@value}
105     */
106    public static final String ATTRIBUTE_ANNOTATION_DEFAULT = "AnnotationDefault"; //$NON-NLS-1$
107
108    /**
109     * {@value}
110     */
111    public static final String ATTRIBUTE_CLASS_FILE_VERSION = "class-file version"; //$NON-NLS-1$
112
113    /**
114     * {@value}
115     */
116    public static final String ATTRIBUTE_CODE = "Code"; //$NON-NLS-1$
117
118    /**
119     * {@value}
120     */
121    public static final String ATTRIBUTE_CONSTANT_VALUE = "ConstantValue"; //$NON-NLS-1$
122
123    /**
124     * {@value}
125     */
126    public static final String ATTRIBUTE_DEPRECATED = "Deprecated"; //$NON-NLS-1$
127
128    /**
129     * {@value}
130     */
131
132    /**
133     * {@value}
134     */
135    public static final String ATTRIBUTE_ENCLOSING_METHOD = "EnclosingMethod"; //$NON-NLS-1$
136
137    /**
138     * {@value}
139     */
140    public static final String ATTRIBUTE_EXCEPTIONS = "Exceptions"; //$NON-NLS-1$
141
142    /**
143     * {@value}
144     */
145    public static final String ATTRIBUTE_INNER_CLASSES = "InnerClasses"; //$NON-NLS-1$
146
147    /**
148     * {@value}
149     */
150    public static final String ATTRIBUTE_LINE_NUMBER_TABLE = "LineNumberTable"; //$NON-NLS-1$
151
152    /**
153     * {@value}
154     */
155    public static final String ATTRIBUTE_LOCAL_VARIABLE_TABLE = "LocalVariableTable"; //$NON-NLS-1$
156
157    /**
158     * {@value}
159     */
160    public static final String ATTRIBUTE_LOCAL_VARIABLE_TYPE_TABLE = "LocalVariableTypeTable"; //$NON-NLS-1$
161
162    /**
163     * {@value}
164     */
165    public static final String ATTRIBUTE_RUNTIME_INVISIBLE_ANNOTATIONS = "RuntimeInvisibleAnnotations"; //$NON-NLS-1$
166
167    /**
168     * {@value}
169     */
170    public static final String ATTRIBUTE_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS = "RuntimeInvisibleParameterAnnotations"; //$NON-NLS-1$
171
172    /**
173     * {@value}
174     */
175    public static final String ATTRIBUTE_RUNTIME_VISIBLE_ANNOTATIONS = "RuntimeVisibleAnnotations"; //$NON-NLS-1$
176
177    /**
178     * {@value}
179     */
180    public static final String ATTRIBUTE_RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS = "RuntimeVisibleParameterAnnotations"; //$NON-NLS-1$
181
182    /**
183     * {@value}
184     */
185    public static final String ATTRIBUTE_SIGNATURE = "Signature"; //$NON-NLS-1$
186
187    /**
188     * {@value}
189     */
190    public static final String ATTRIBUTE_SOURCE_FILE = "SourceFile"; //$NON-NLS-1$
191
192    /**
193     * {@value}
194     */
195    public static final int CONTEXT_CLASS = 0;
196
197    /**
198     * {@value}
199     */
200    public static final int CONTEXT_CODE = 3;
201
202    /**
203     * {@value}
204     */
205    public static final int CONTEXT_FIELD = 1;
206
207    /**
208     * {@value}
209     */
210    public static final int CONTEXT_METHOD = 2;
211
212    /**
213     * Context names.
214     */
215    public static final String[] contextNames = {"Class", "Field", "Method", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
216        "Code",}; //$NON-NLS-1$
217
218    private static ClassFileEntry getValue(final String layout, long value, final SegmentConstantPool pool)
219        throws Pack200Exception {
220        if (layout.startsWith("R")) { //$NON-NLS-1$
221            // references
222            if (layout.indexOf('N') != -1) {
223                value--;
224            }
225            if (layout.startsWith("RU")) { //$NON-NLS-1$
226                return pool.getValue(SegmentConstantPool.UTF_8, value);
227            }
228            if (layout.startsWith("RS")) { //$NON-NLS-1$
229                return pool.getValue(SegmentConstantPool.SIGNATURE, value);
230            }
231        } else if (layout.startsWith("K")) { //$NON-NLS-1$
232            final char type = layout.charAt(1);
233            switch (type) {
234            case 'S': // String
235                return pool.getValue(SegmentConstantPool.CP_STRING, value);
236            case 'I': // Int (or byte or short)
237            case 'C': // Char
238                return pool.getValue(SegmentConstantPool.CP_INT, value);
239            case 'F': // Float
240                return pool.getValue(SegmentConstantPool.CP_FLOAT, value);
241            case 'J': // Long
242                return pool.getValue(SegmentConstantPool.CP_LONG, value);
243            case 'D': // Double
244                return pool.getValue(SegmentConstantPool.CP_DOUBLE, value);
245            }
246        }
247        throw new Pack200Exception("Unknown layout encoding: " + layout);
248    }
249
250    private final int context;
251
252    private final int index;
253
254    private final String layout;
255
256    private long mask;
257
258    private final String name;
259    private final boolean isDefault;
260    private int backwardsCallCount;
261
262    /**
263     * Constructs a default AttributeLayout (equivalent to
264     * {@code new AttributeLayout(name, context, layout, index, true);})
265     *
266     * @param name TODO
267     * @param context TODO
268     * @param layout TODO
269     * @param index TODO
270     * @throws Pack200Exception Attribute context out of range.
271     * @throws Pack200Exception Cannot have a null layout.
272     * @throws Pack200Exception Cannot have an unnamed layout.
273     */
274    public AttributeLayout(final String name, final int context, final String layout, final int index)
275        throws Pack200Exception {
276        this(name, context, layout, index, true);
277    }
278
279    public AttributeLayout(final String name, final int context, final String layout, final int index,
280        final boolean isDefault) throws Pack200Exception {
281        this.index = index;
282        this.context = context;
283        if (index >= 0) {
284            this.mask = 1L << index;
285        } else {
286            this.mask = 0;
287        }
288        if (context != CONTEXT_CLASS && context != CONTEXT_CODE && context != CONTEXT_FIELD
289            && context != CONTEXT_METHOD) {
290            throw new Pack200Exception("Attribute context out of range: " + context);
291        }
292        if (layout == null) {
293            throw new Pack200Exception("Cannot have a null layout");
294        }
295        if (name == null || name.length() == 0) {
296            throw new Pack200Exception("Cannot have an unnamed layout");
297        }
298        this.name = name;
299        this.layout = layout;
300        this.isDefault = isDefault;
301    }
302
303    public Codec getCodec() {
304        if (layout.indexOf('O') >= 0) {
305            return Codec.BRANCH5;
306        }
307        if (layout.indexOf('P') >= 0) {
308            return Codec.BCI5;
309        }
310        if (layout.indexOf('S') >= 0 && layout.indexOf("KS") < 0 //$NON-NLS-1$
311            && layout.indexOf("RS") < 0) { //$NON-NLS-1$
312            return Codec.SIGNED5;
313        }
314        if (layout.indexOf('B') >= 0) {
315            return Codec.BYTE1;
316        }
317        return Codec.UNSIGNED5;
318    }
319
320    public int getContext() {
321        return context;
322    }
323
324    public int getIndex() {
325        return index;
326    }
327
328    public String getLayout() {
329        return layout;
330    }
331
332    public String getName() {
333        return name;
334    }
335
336    public ClassFileEntry getValue(final long value, final SegmentConstantPool pool) throws Pack200Exception {
337        return getValue(layout, value, pool);
338    }
339
340    public ClassFileEntry getValue(final long value, final String type, final SegmentConstantPool pool)
341        throws Pack200Exception {
342        // TODO This really needs to be better tested, esp. the different types
343        // TODO This should have the ability to deal with RUN stuff too, and
344        // unions
345        if (!layout.startsWith("KQ")) {
346            return getValue(layout, value, pool);
347        }
348        if (type.equals("Ljava/lang/String;")) { //$NON-NLS-1$
349            return getValue("KS", value, pool);
350        }
351        return getValue("K" + type + layout.substring(2), value, //$NON-NLS-1$
352            pool);
353    }
354
355    @Override
356    public int hashCode() {
357        final int PRIME = 31;
358        int r = 1;
359        if (name != null) {
360            r = r * PRIME + name.hashCode();
361        }
362        if (layout != null) {
363            r = r * PRIME + layout.hashCode();
364        }
365        r = r * PRIME + index;
366        r = r * PRIME + context;
367        return r;
368    }
369
370    public boolean isDefaultLayout() {
371        return isDefault;
372    }
373
374    /*
375     * (non-Javadoc)
376     *
377     * @see org.apache.commons.compress.harmony.unpack200.IMatches#matches(long)
378     */
379    @Override
380    public boolean matches(final long value) {
381        return (value & mask) != 0;
382    }
383
384    public int numBackwardsCallables() {
385        if ("*".equals(layout)) {
386            return 1;
387        }
388        return backwardsCallCount;
389    }
390
391    public void setBackwardsCallCount(final int backwardsCallCount) {
392        this.backwardsCallCount = backwardsCallCount;
393    }
394
395    @Override
396    public String toString() {
397        return contextNames[context] + ": " + name;
398    }
399
400}