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