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) throws Pack200Exception {
219        if (layout.startsWith("R")) { //$NON-NLS-1$
220            // references
221            if (layout.indexOf('N') != -1) {
222                value--;
223            }
224            if (layout.startsWith("RU")) { //$NON-NLS-1$
225                return pool.getValue(SegmentConstantPool.UTF_8, value);
226            }
227            if (layout.startsWith("RS")) { //$NON-NLS-1$
228                return pool.getValue(SegmentConstantPool.SIGNATURE, value);
229            }
230        } else if (layout.startsWith("K")) { //$NON-NLS-1$
231            final char type = layout.charAt(1);
232            switch (type) {
233            case 'S': // String
234                return pool.getValue(SegmentConstantPool.CP_STRING, value);
235            case 'I': // Int (or byte or short)
236            case 'C': // Char
237                return pool.getValue(SegmentConstantPool.CP_INT, value);
238            case 'F': // Float
239                return pool.getValue(SegmentConstantPool.CP_FLOAT, value);
240            case 'J': // Long
241                return pool.getValue(SegmentConstantPool.CP_LONG, value);
242            case 'D': // Double
243                return pool.getValue(SegmentConstantPool.CP_DOUBLE, value);
244            }
245        }
246        throw new Pack200Exception("Unknown layout encoding: " + layout);
247    }
248
249    private final int context;
250
251    private final int index;
252
253    private final String layout;
254
255    private long mask;
256
257    private final String name;
258    private final boolean isDefault;
259    private int backwardsCallCount;
260
261    /**
262     * Constructs a default AttributeLayout (equivalent to {@code new AttributeLayout(name, context, layout, index, true);})
263     *
264     * @param name    TODO
265     * @param context TODO
266     * @param layout  TODO
267     * @param index   TODO
268     * @throws Pack200Exception Attribute context out of range.
269     * @throws Pack200Exception Cannot have a null layout.
270     * @throws Pack200Exception Cannot have an unnamed layout.
271     */
272    public AttributeLayout(final String name, final int context, final String layout, final int index) throws Pack200Exception {
273        this(name, context, layout, index, true);
274    }
275
276    public AttributeLayout(final String name, final int context, final String layout, final int index, final boolean isDefault) throws Pack200Exception {
277        this.index = index;
278        this.context = context;
279        if (index >= 0) {
280            this.mask = 1L << index;
281        } else {
282            this.mask = 0;
283        }
284        if (context != CONTEXT_CLASS && context != CONTEXT_CODE && context != CONTEXT_FIELD && context != CONTEXT_METHOD) {
285            throw new Pack200Exception("Attribute context out of range: " + context);
286        }
287        if (layout == null) {
288            throw new Pack200Exception("Cannot have a null layout");
289        }
290        if (name == null || name.length() == 0) {
291            throw new Pack200Exception("Cannot have an unnamed layout");
292        }
293        this.name = name;
294        this.layout = layout;
295        this.isDefault = isDefault;
296    }
297
298    public Codec getCodec() {
299        if (layout.indexOf('O') >= 0) {
300            return Codec.BRANCH5;
301        }
302        if (layout.indexOf('P') >= 0) {
303            return Codec.BCI5;
304        }
305        if (layout.indexOf('S') >= 0 && !layout.contains("KS") //$NON-NLS-1$
306                && !layout.contains("RS")) { //$NON-NLS-1$
307            return Codec.SIGNED5;
308        }
309        if (layout.indexOf('B') >= 0) {
310            return Codec.BYTE1;
311        }
312        return Codec.UNSIGNED5;
313    }
314
315    public int getContext() {
316        return context;
317    }
318
319    public int getIndex() {
320        return index;
321    }
322
323    public String getLayout() {
324        return layout;
325    }
326
327    public String getName() {
328        return name;
329    }
330
331    public ClassFileEntry getValue(final long value, final SegmentConstantPool pool) throws Pack200Exception {
332        return getValue(layout, value, pool);
333    }
334
335    public ClassFileEntry getValue(final long value, final String type, final SegmentConstantPool pool) throws Pack200Exception {
336        // TODO This really needs to be better tested, esp. the different types
337        // TODO This should have the ability to deal with RUN stuff too, and
338        // unions
339        if (!layout.startsWith("KQ")) {
340            return getValue(layout, value, pool);
341        }
342        if (type.equals("Ljava/lang/String;")) { //$NON-NLS-1$
343            return getValue("KS", value, pool);
344        }
345        return getValue("K" + type + layout.substring(2), value, //$NON-NLS-1$
346                pool);
347    }
348
349    @Override
350    public int hashCode() {
351        final int PRIME = 31;
352        int r = 1;
353        if (name != null) {
354            r = r * PRIME + name.hashCode();
355        }
356        if (layout != null) {
357            r = r * PRIME + layout.hashCode();
358        }
359        r = r * PRIME + index;
360        r = r * PRIME + context;
361        return r;
362    }
363
364    public boolean isDefaultLayout() {
365        return isDefault;
366    }
367
368    /*
369     * (non-Javadoc)
370     *
371     * @see org.apache.commons.compress.harmony.unpack200.IMatches#matches(long)
372     */
373    @Override
374    public boolean matches(final long value) {
375        return (value & mask) != 0;
376    }
377
378    public int numBackwardsCallables() {
379        if ("*".equals(layout)) {
380            return 1;
381        }
382        return backwardsCallCount;
383    }
384
385    public void setBackwardsCallCount(final int backwardsCallCount) {
386        this.backwardsCallCount = backwardsCallCount;
387    }
388
389    @Override
390    public String toString() {
391        return contextNames[context] + ": " + name;
392    }
393
394}