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