/* * Copyright (C) 2007 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.dx.cf.direct; import com.android.dx.cf.iface.Attribute; import com.android.dx.cf.iface.ParseException; import com.android.dx.cf.iface.ParseObserver; import com.android.dx.cf.iface.StdAttributeList; import com.android.dx.util.ByteArray; import com.android.dx.util.Hex; /** * Parser for lists of attributes. */ final /*package*/ class AttributeListParser { /** {@code non-null;} the class file to parse from */ private final DirectClassFile cf; /** attribute parsing context */ private final int context; /** offset in the byte array of the classfile to the start of the list */ private final int offset; /** {@code non-null;} attribute factory to use */ private final AttributeFactory attributeFactory; /** {@code non-null;} list of parsed attributes */ private final StdAttributeList list; /** {@code >= -1;} the end offset of this list in the byte array of the * classfile, or {@code -1} if not yet parsed */ private int endOffset; /** {@code null-ok;} parse observer, if any */ private ParseObserver observer; /** * Constructs an instance. * * @param cf {@code non-null;} class file to parse from * @param context attribute parsing context (see {@link AttributeFactory}) * @param offset offset in {@code bytes} to the start of the list * @param attributeFactory {@code non-null;} attribute factory to use */ public AttributeListParser(DirectClassFile cf, int context, int offset, AttributeFactory attributeFactory) { if (cf == null) { throw new NullPointerException("cf == null"); } if (attributeFactory == null) { throw new NullPointerException("attributeFactory == null"); } int size = cf.getBytes().getUnsignedShort(offset); this.cf = cf; this.context = context; this.offset = offset; this.attributeFactory = attributeFactory; this.list = new StdAttributeList(size); this.endOffset = -1; } /** * Sets the parse observer for this instance. * * @param observer {@code null-ok;} the observer */ public void setObserver(ParseObserver observer) { this.observer = observer; } /** * Gets the end offset of this constant pool in the {@code byte[]} * which it came from. * * @return {@code >= 0;} the end offset */ public int getEndOffset() { parseIfNecessary(); return endOffset; } /** * Gets the parsed list. * * @return {@code non-null;} the list */ public StdAttributeList getList() { parseIfNecessary(); return list; } /** * Runs {@link #parse} if it has not yet been run successfully. */ private void parseIfNecessary() { if (endOffset < 0) { parse(); } } /** * Does the actual parsing. */ private void parse() { int sz = list.size(); int at = offset + 2; // Skip the count. ByteArray bytes = cf.getBytes(); if (observer != null) { observer.parsed(bytes, offset, 2, "attributes_count: " + Hex.u2(sz)); } for (int i = 0; i < sz; i++) { try { if (observer != null) { observer.parsed(bytes, at, 0, "\nattributes[" + i + "]:\n"); observer.changeIndent(1); } Attribute attrib = attributeFactory.parse(cf, context, at, observer); at += attrib.byteLength(); list.set(i, attrib); if (observer != null) { observer.changeIndent(-1); observer.parsed(bytes, at, 0, "end attributes[" + i + "]\n"); } } catch (ParseException ex) { ex.addContext("...while parsing attributes[" + i + "]"); throw ex; } catch (RuntimeException ex) { ParseException pe = new ParseException(ex); pe.addContext("...while parsing attributes[" + i + "]"); throw pe; } } endOffset = at; } }