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}