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