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; 020 021import org.apache.commons.compress.harmony.pack200.Codec; 022import org.apache.commons.compress.harmony.pack200.Pack200Exception; 023import org.apache.commons.compress.harmony.unpack200.bytecode.ClassFileEntry; 024import org.apache.commons.lang3.StringUtils; 025 026/** 027 * Defines a layout that describes how an attribute will be transmitted. 028 */ 029public class AttributeLayout implements IMatcher { 030 031 /** 032 * {@value} 033 */ 034 public static final String ACC_ABSTRACT = "ACC_ABSTRACT"; //$NON-NLS-1$ 035 036 /** 037 * {@value} 038 */ 039 public static final String ACC_ANNOTATION = "ACC_ANNOTATION"; //$NON-NLS-1$ 040 041 /** 042 * {@value} 043 */ 044 public static final String ACC_ENUM = "ACC_ENUM"; //$NON-NLS-1$ 045 046 /** 047 * {@value} 048 */ 049 public static final String ACC_FINAL = "ACC_FINAL"; //$NON-NLS-1$ 050 051 /** 052 * {@value} 053 */ 054 public static final String ACC_INTERFACE = "ACC_INTERFACE"; //$NON-NLS-1$ 055 056 /** 057 * {@value} 058 */ 059 public static final String ACC_NATIVE = "ACC_NATIVE"; //$NON-NLS-1$ 060 061 /** 062 * {@value} 063 */ 064 public static final String ACC_PRIVATE = "ACC_PRIVATE"; //$NON-NLS-1$ 065 066 /** 067 * {@value} 068 */ 069 public static final String ACC_PROTECTED = "ACC_PROTECTED"; //$NON-NLS-1$ 070 071 /** 072 * {@value} 073 */ 074 public static final String ACC_PUBLIC = "ACC_PUBLIC"; //$NON-NLS-1$ 075 076 /** 077 * {@value} 078 */ 079 public static final String ACC_STATIC = "ACC_STATIC"; //$NON-NLS-1$ 080 081 /** 082 * {@value} 083 */ 084 public static final String ACC_STRICT = "ACC_STRICT"; //$NON-NLS-1$ 085 086 /** 087 * {@value} 088 */ 089 public static final String ACC_SYNCHRONIZED = "ACC_SYNCHRONIZED"; //$NON-NLS-1$ 090 091 /** 092 * {@value} 093 */ 094 public static final String ACC_SYNTHETIC = "ACC_SYNTHETIC"; //$NON-NLS-1$ 095 096 /** 097 * {@value} 098 */ 099 public static final String ACC_TRANSIENT = "ACC_TRANSIENT"; //$NON-NLS-1$ 100 101 /** 102 * {@value} 103 */ 104 public static final String ACC_VOLATILE = "ACC_VOLATILE"; //$NON-NLS-1$ 105 106 /** 107 * {@value} 108 */ 109 public static final String ATTRIBUTE_ANNOTATION_DEFAULT = "AnnotationDefault"; //$NON-NLS-1$ 110 111 /** 112 * {@value} 113 */ 114 public static final String ATTRIBUTE_CLASS_FILE_VERSION = "class-file version"; //$NON-NLS-1$ 115 116 /** 117 * {@value} 118 */ 119 public static final String ATTRIBUTE_CODE = "Code"; //$NON-NLS-1$ 120 121 /** 122 * {@value} 123 */ 124 public static final String ATTRIBUTE_CONSTANT_VALUE = "ConstantValue"; //$NON-NLS-1$ 125 126 /** 127 * {@value} 128 */ 129 public static final String ATTRIBUTE_DEPRECATED = "Deprecated"; //$NON-NLS-1$ 130 131 /** 132 * {@value} 133 */ 134 public static final String ATTRIBUTE_ENCLOSING_METHOD = "EnclosingMethod"; //$NON-NLS-1$ 135 136 /** 137 * {@value} 138 */ 139 public static final String ATTRIBUTE_EXCEPTIONS = "Exceptions"; //$NON-NLS-1$ 140 141 /** 142 * {@value} 143 */ 144 public static final String ATTRIBUTE_INNER_CLASSES = "InnerClasses"; //$NON-NLS-1$ 145 146 /** 147 * {@value} 148 */ 149 public static final String ATTRIBUTE_LINE_NUMBER_TABLE = "LineNumberTable"; //$NON-NLS-1$ 150 151 /** 152 * {@value} 153 */ 154 public static final String ATTRIBUTE_LOCAL_VARIABLE_TABLE = "LocalVariableTable"; //$NON-NLS-1$ 155 156 /** 157 * {@value} 158 */ 159 public static final String ATTRIBUTE_LOCAL_VARIABLE_TYPE_TABLE = "LocalVariableTypeTable"; //$NON-NLS-1$ 160 161 /** 162 * {@value} 163 */ 164 public static final String ATTRIBUTE_RUNTIME_INVISIBLE_ANNOTATIONS = "RuntimeInvisibleAnnotations"; //$NON-NLS-1$ 165 166 /** 167 * {@value} 168 */ 169 public static final String ATTRIBUTE_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS = "RuntimeInvisibleParameterAnnotations"; //$NON-NLS-1$ 170 171 /** 172 * {@value} 173 */ 174 public static final String ATTRIBUTE_RUNTIME_VISIBLE_ANNOTATIONS = "RuntimeVisibleAnnotations"; //$NON-NLS-1$ 175 176 /** 177 * {@value} 178 */ 179 public static final String ATTRIBUTE_RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS = "RuntimeVisibleParameterAnnotations"; //$NON-NLS-1$ 180 181 /** 182 * {@value} 183 */ 184 public static final String ATTRIBUTE_SIGNATURE = "Signature"; //$NON-NLS-1$ 185 186 /** 187 * {@value} 188 */ 189 public static final String ATTRIBUTE_SOURCE_FILE = "SourceFile"; //$NON-NLS-1$ 190 191 /** 192 * {@value} 193 */ 194 public static final int CONTEXT_CLASS = 0; 195 196 /** 197 * {@value} 198 */ 199 public static final int CONTEXT_CODE = 3; 200 201 /** 202 * {@value} 203 */ 204 public static final int CONTEXT_FIELD = 1; 205 206 /** 207 * {@value} 208 */ 209 public static final int CONTEXT_METHOD = 2; 210 211 /** 212 * Context names. 213 */ 214 public static final String[] contextNames = { "Class", "Field", "Method", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ 215 "Code", }; //$NON-NLS-1$ 216 217 private static ClassFileEntry getValue(final String layout, long longIndex, final SegmentConstantPool pool) throws Pack200Exception { 218 if (layout.startsWith("R")) { //$NON-NLS-1$ 219 // references 220 if (layout.indexOf('N') != -1) { 221 longIndex--; 222 } 223 if (layout.startsWith("RU")) { //$NON-NLS-1$ 224 return pool.getValue(SegmentConstantPool.UTF_8, longIndex); 225 } 226 if (layout.startsWith("RS")) { //$NON-NLS-1$ 227 return pool.getValue(SegmentConstantPool.SIGNATURE, longIndex); 228 } 229 } else if (layout.startsWith("K")) { //$NON-NLS-1$ 230 final char type = layout.charAt(1); 231 switch (type) { 232 case 'S': // String 233 return pool.getValue(SegmentConstantPool.CP_STRING, longIndex); 234 case 'I': // Int (or byte or short) 235 case 'C': // Char 236 return pool.getValue(SegmentConstantPool.CP_INT, longIndex); 237 case 'F': // Float 238 return pool.getValue(SegmentConstantPool.CP_FLOAT, longIndex); 239 case 'J': // Long 240 return pool.getValue(SegmentConstantPool.CP_LONG, longIndex); 241 case 'D': // Double 242 return pool.getValue(SegmentConstantPool.CP_DOUBLE, longIndex); 243 } 244 } 245 throw new Pack200Exception("Unknown layout encoding: " + layout); 246 } 247 248 private final int context; 249 250 private final int index; 251 252 private final String layout; 253 254 private long mask; 255 256 private final String name; 257 private final boolean isDefault; 258 private int backwardsCallCount; 259 260 /** 261 * Constructs a default AttributeLayout (equivalent to {@code new AttributeLayout(name, context, layout, index, true);}) 262 * 263 * @param name The layout name. 264 * @param context One of {@link #CONTEXT_CLASS}, {@link #CONTEXT_CODE}, {@link #CONTEXT_FIELD}, {@link #CONTEXT_METHOD}. 265 * @param layout The layout. 266 * @param index The index, currently used as part of computing the hash code. 267 * @throws Pack200Exception Attribute context out of range. 268 * @throws Pack200Exception Cannot have a null layout. 269 * @throws Pack200Exception Cannot have an unnamed layout. 270 */ 271 public AttributeLayout(final String name, final int context, final String layout, final int index) throws Pack200Exception { 272 this(name, context, layout, index, true); 273 } 274 275 /** 276 * Constructs a default AttributeLayout (equivalent to {@code new AttributeLayout(name, context, layout, index, true);}) 277 * 278 * @param name The layout name. 279 * @param context One of {@link #CONTEXT_CLASS}, {@link #CONTEXT_CODE}, {@link #CONTEXT_FIELD}, {@link #CONTEXT_METHOD}. 280 * @param layout The layout. 281 * @param index The index, currently used as part of computing the hash code. 282 * @param isDefault Whether this is the default layout. 283 * @throws Pack200Exception Attribute context out of range. 284 * @throws Pack200Exception Cannot have a null layout. 285 * @throws Pack200Exception Cannot have an unnamed layout. 286 */ 287 public AttributeLayout(final String name, final int context, final String layout, final int index, final boolean isDefault) throws Pack200Exception { 288 this.index = index; 289 this.context = context; 290 if (index >= 0) { 291 this.mask = 1L << index; 292 } else { 293 this.mask = 0; 294 } 295 if (context != CONTEXT_CLASS && context != CONTEXT_CODE && context != CONTEXT_FIELD && context != CONTEXT_METHOD) { 296 throw new Pack200Exception("Attribute context out of range: " + context); 297 } 298 if (layout == null) { 299 throw new Pack200Exception("Cannot have a null layout"); 300 } 301 if (StringUtils.isEmpty(name)) { 302 throw new Pack200Exception("Cannot have an unnamed layout"); 303 } 304 this.name = name; 305 this.layout = layout; 306 this.isDefault = isDefault; 307 } 308 309 /** 310 * Gets the Codec based on the layout. 311 * 312 * @return the Codec. 313 */ 314 public Codec getCodec() { 315 if (layout.indexOf('O') >= 0) { 316 return Codec.BRANCH5; 317 } 318 if (layout.indexOf('P') >= 0) { 319 return Codec.BCI5; 320 } 321 if (layout.indexOf('S') >= 0 && !layout.contains("KS") //$NON-NLS-1$ 322 && !layout.contains("RS")) { //$NON-NLS-1$ 323 return Codec.SIGNED5; 324 } 325 if (layout.indexOf('B') >= 0) { 326 return Codec.BYTE1; 327 } 328 return Codec.UNSIGNED5; 329 } 330 331 /** 332 * Gets the context. 333 * 334 * @return the context. 335 */ 336 public int getContext() { 337 return context; 338 } 339 340 /** 341 * Gets the index. 342 * 343 * @return the index. 344 */ 345 public int getIndex() { 346 return index; 347 } 348 349 /** 350 * Gets the layout. 351 * 352 * @return the layout. 353 */ 354 public String getLayout() { 355 return layout; 356 } 357 358 /** 359 * Gets the name. 360 * 361 * @return the name. 362 */ 363 public String getName() { 364 return name; 365 } 366 367 /** 368 * Gets the ClassFileEntry for the given input. 369 * 370 * @param longIndex An index into the segment constant pool. 371 * @param pool the segment constant pool. 372 * @return the matching ClassFileEntry. 373 * @throws Pack200Exception if the input is invalid. 374 */ 375 public ClassFileEntry getValue(final long longIndex, final SegmentConstantPool pool) throws Pack200Exception { 376 return getValue(layout, longIndex, pool); 377 } 378 379 /** 380 * Gets the ClassFileEntry for the given input. 381 * 382 * @param longIndex An index into the segment constant pool. 383 * @param type the Java type signature. 384 * @param pool the segment constant pool. 385 * @return the matching ClassFileEntry. 386 * @throws Pack200Exception if the input is invalid. 387 */ 388 public ClassFileEntry getValue(final long longIndex, final String type, final SegmentConstantPool pool) throws Pack200Exception { 389 // TODO This really needs to be better tested, esp. the different types 390 // TODO This should have the ability to deal with RUN stuff too, and 391 // unions 392 if (!layout.startsWith("KQ")) { 393 return getValue(layout, longIndex, pool); 394 } 395 if (type.equals("Ljava/lang/String;")) { //$NON-NLS-1$ 396 return getValue("KS", longIndex, pool); 397 } 398 return getValue("K" + type + layout.substring(2), longIndex, //$NON-NLS-1$ 399 pool); 400 } 401 402 @Override 403 public int hashCode() { 404 final int prime = 31; 405 int r = 1; 406 if (name != null) { 407 r = r * prime + name.hashCode(); 408 } 409 if (layout != null) { 410 r = r * prime + layout.hashCode(); 411 } 412 r = r * prime + index; 413 r = r * prime + context; 414 return r; 415 } 416 417 /** 418 * Tests whether this is the default layout. 419 * 420 * @return whether this is the default layout. 421 */ 422 public boolean isDefaultLayout() { 423 return isDefault; 424 } 425 426 /* 427 * (non-Javadoc) 428 * 429 * @see org.apache.commons.compress.harmony.unpack200.IMatches#matches(long) 430 */ 431 @Override 432 public boolean matches(final long value) { 433 return (value & mask) != 0; 434 } 435 436 /** 437 * Gets the backward call count. 438 * 439 * @return the backward call count. 440 */ 441 public int numBackwardsCallables() { 442 if ("*".equals(layout)) { 443 return 1; 444 } 445 return backwardsCallCount; 446 } 447 448 /** 449 * Sets the backward call count. 450 * 451 * @param backwardsCallCount the backward call count. 452 */ 453 public void setBackwardsCallCount(final int backwardsCallCount) { 454 this.backwardsCallCount = backwardsCallCount; 455 } 456 457 @Override 458 public String toString() { 459 return contextNames[context] + ": " + name; 460 } 461 462}