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}