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}