    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.bytecode;
    018
    019import java.io.DataOutputStream;
    020import java.io.IOException;
    021import java.util.ArrayList;
    022import java.util.Iterator;
    023import java.util.List;
    024
    025/**
    026 * A compressor-defined class file attribute.
    027 */
    028public class NewAttribute extends BCIRenumberedAttribute {
    029
    030    private final List lengths = new ArrayList(); // List of Integers
    031    private final List body = new ArrayList();
    032    private ClassConstantPool pool;
    033    private final int layoutIndex;
    034
    035    public NewAttribute(final CPUTF8 attributeName, final int layoutIndex) {
    036        super(attributeName);
    037        this.layoutIndex = layoutIndex;
    038    }
    039
    040    public int getLayoutIndex() {
    041        return layoutIndex;
    042    }
    043
    044    /*
    045     * (non-Javadoc)
    046     *
    047     * @see org.apache.commons.compress.harmony.unpack200.bytecode.Attribute#getLength()
    048     */
    049    @Override
    050    protected int getLength() {
    051        int length = 0;
    052        for (int iter = 0; iter < lengths.size(); iter++) {
    053            length += ((Integer) lengths.get(iter)).intValue();
    054        }
    055        return length;
    056    }
    057
    058    /*
    059     * (non-Javadoc)
    060     *
    061     * @see org.apache.commons.compress.harmony.unpack200.bytecode.Attribute#writeBody(java.io.DataOutputStream)
    062     */
    063    @Override
    064    protected void writeBody(final DataOutputStream dos) throws IOException {
    065        for (int i = 0; i < lengths.size(); i++) {
    066            final int length = ((Integer) lengths.get(i)).intValue();
    067            final Object obj = body.get(i);
    068            long value = 0;
    069            if (obj instanceof Long) {
    070                value = ((Long) obj).longValue();
    071            } else if (obj instanceof ClassFileEntry) {
    072                value = pool.indexOf(((ClassFileEntry) obj));
    073            } else if (obj instanceof BCValue) {
    074                value = ((BCValue) obj).actualValue;
    075            }
    076            // Write
    077            if (length == 1) {
    078                dos.writeByte((int) value);
    079            } else if (length == 2) {
    080                dos.writeShort((int) value);
    081            } else if (length == 4) {
    082                dos.writeInt((int) value);
    083            } else if (length == 8) {
    084                dos.writeLong(value);
    085            }
    086        }
    087    }
    088
    089    /*
    090     * (non-Javadoc)
    091     *
    092     * @see org.apache.commons.compress.harmony.unpack200.bytecode.ClassFileEntry#toString()
    093     */
    094    @Override
    095    public String toString() {
    096        return attributeName.underlyingString();
    097    }
    098
    099    public void addInteger(final int length, final long value) {
    100        lengths.add(Integer.valueOf(length));
    101        body.add(Long.valueOf(value));
    102    }
    103
    104    public void addBCOffset(final int length, final int value) {
    105        lengths.add(Integer.valueOf(length));
    106        body.add(new BCOffset(value));
    107    }
    108
    109    public void addBCIndex(final int length, final int value) {
    110        lengths.add(Integer.valueOf(length));
    111        body.add(new BCIndex(value));
    112    }
    113
    114    public void addBCLength(final int length, final int value) {
    115        lengths.add(Integer.valueOf(length));
    116        body.add(new BCLength(value));
    117    }
    118
    119    public void addToBody(final int length, final Object value) {
    120        lengths.add(Integer.valueOf(length));
    121        body.add(value);
    122    }
    123
    124    @Override
    125    protected void resolve(final ClassConstantPool pool) {
    126        super.resolve(pool);
    127        for (int iter = 0; iter < body.size(); iter++) {
    128            final Object element = body.get(iter);
    129            if (element instanceof ClassFileEntry) {
    130                ((ClassFileEntry) element).resolve(pool);
    131            }
    132        }
    133        this.pool = pool;
    134    }
    135
    136    @Override
    137    protected ClassFileEntry[] getNestedClassFileEntries() {
    138        int total = 1;
    139        for (int iter = 0; iter < body.size(); iter++) {
    140            final Object element = body.get(iter);
    141            if (element instanceof ClassFileEntry) {
    142                total++;
    143            }
    144        }
    145        final ClassFileEntry[] nested = new ClassFileEntry[total];
    146        nested[0] = getAttributeName();
    147        int i = 1;
    148        for (int iter = 0; iter < body.size(); iter++) {
    149            final Object element = body.get(iter);
    150            if (element instanceof ClassFileEntry) {
    151                nested[i] = (ClassFileEntry) element;
    152                i++;
    153            }
    154        }
    155        return nested;
    156    }
    157
    158    private static class BCOffset extends BCValue {
    159
    160        private final int offset;
    161        private int index;
    162
    163        public BCOffset(final int offset) {
    164            this.offset = offset;
    165        }
    166
    167        public void setIndex(final int index) {
    168            this.index = index;
    169        }
    170
    171    }
    172
    173    private static class BCIndex extends BCValue {
    174
    175        private final int index;
    176
    177        public BCIndex(final int index) {
    178            this.index = index;
    179        }
    180    }
    181
    182    private static class BCLength extends BCValue {
    183
    184        private final int length;
    185
    186        public BCLength(final int length) {
    187            this.length = length;
    188        }
    189    }
    190
    191    // Bytecode-related value (either a bytecode index or a length)
    192    private static abstract class BCValue {
    193
    194        int actualValue;
    195
    196        public void setActualValue(final int value) {
    197            this.actualValue = value;
    198        }
    199
    200    }
    201
    202    @Override
    203    protected int[] getStartPCs() {
    204        // Don't need to return anything here as we've overridden renumber
    205        return null;
    206    }
    207
    208    @Override
    209    public void renumber(final List byteCodeOffsets) {
    210        if (!renumbered) {
    211            Object previous = null;
    212            for (final Iterator iter = body.iterator(); iter.hasNext();) {
    213                final Object obj = iter.next();
    214                if (obj instanceof BCIndex) {
    215                    final BCIndex bcIndex = (BCIndex) obj;
    216                    bcIndex.setActualValue(((Integer) byteCodeOffsets.get(bcIndex.index)).intValue());
    217                } else if (obj instanceof BCOffset) {
    218                    final BCOffset bcOffset = (BCOffset) obj;
    219                    if (previous instanceof BCIndex) {
    220                        final int index = ((BCIndex) previous).index + bcOffset.offset;
    221                        bcOffset.setIndex(index);
    222                        bcOffset.setActualValue(((Integer) byteCodeOffsets.get(index)).intValue());
    223                    } else if (previous instanceof BCOffset) {
    224                        final int index = ((BCOffset) previous).index + bcOffset.offset;
    225                        bcOffset.setIndex(index);
    226                        bcOffset.setActualValue(((Integer) byteCodeOffsets.get(index)).intValue());
    227                    } else {
    228                        // Not sure if this should be able to happen
    229                        bcOffset.setActualValue(((Integer) byteCodeOffsets.get(bcOffset.offset)).intValue());
    230                    }
    231                } else if (obj instanceof BCLength) {
    232                    // previous must be a BCIndex
    233                    final BCLength bcLength = (BCLength) obj;
    234                    final BCIndex prevIndex = (BCIndex) previous;
    235                    final int index = prevIndex.index + bcLength.length;
    236                    final int actualLength = ((Integer) byteCodeOffsets.get(index)).intValue() - prevIndex.actualValue;
    237                    bcLength.setActualValue(actualLength);
    238                }
    239                previous = obj;
    240            }
    241            renumbered = true;
    242        }
    243    }
    244
    245}