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}