001: /*
002: * Copyright 2004 Brian S O'Neill
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.cojen.classfile.attribute;
018:
019: import java.util.ArrayList;
020: import java.util.Collections;
021: import java.util.List;
022: import java.util.Set;
023: import java.util.TreeSet;
024: import java.io.DataInput;
025: import java.io.DataOutput;
026: import java.io.IOException;
027: import org.cojen.classfile.Attribute;
028: import org.cojen.classfile.ConstantPool;
029: import org.cojen.classfile.FixedLocation;
030: import org.cojen.classfile.Location;
031:
032: /**
033: * This class corresponds to the LineNumberTable_attribute structure as
034: * defined in section 4.7.6 of <i>The Java Virtual Machine Specification</i>.
035: *
036: * @author Brian S O'Neill
037: */
038: public class LineNumberTableAttr extends Attribute {
039:
040: private List mEntries = new ArrayList();
041: private boolean mClean = false;
042:
043: public LineNumberTableAttr(ConstantPool cp) {
044: super (cp, LINE_NUMBER_TABLE);
045: }
046:
047: public LineNumberTableAttr(ConstantPool cp, String name) {
048: super (cp, name);
049: }
050:
051: public LineNumberTableAttr(ConstantPool cp, String name,
052: int length, DataInput din) throws IOException {
053: super (cp, name);
054:
055: int size = din.readUnsignedShort();
056: for (int i = 0; i < size; i++) {
057: int start_pc = din.readUnsignedShort();
058: int line_number = din.readUnsignedShort();
059:
060: try {
061: addEntry(new FixedLocation(start_pc), line_number);
062: } catch (IllegalArgumentException e) {
063: }
064: }
065: }
066:
067: public int getLineNumber(Location start) {
068: clean();
069: int index = Collections.binarySearch(mEntries, new Entry(start,
070: 0));
071: if (index < 0) {
072: if ((index = -index - 2) < 0) {
073: return -1;
074: }
075: }
076: return ((Entry) mEntries.get(index)).mLineNumber;
077: }
078:
079: public void addEntry(Location start, int line_number)
080: throws IllegalArgumentException {
081: if (line_number < 0 || line_number > 65535) {
082: throw new IllegalArgumentException(
083: "Value for line number out of " + "valid range: "
084: + line_number);
085: }
086: mEntries.add(new Entry(start, line_number));
087: mClean = false;
088: }
089:
090: public int getLength() {
091: clean();
092: return 2 + 4 * mEntries.size();
093: }
094:
095: public void writeDataTo(DataOutput dout) throws IOException {
096: int size = mEntries.size();
097: dout.writeShort(size);
098: for (int i = 0; i < size; i++) {
099: Entry entry = (Entry) mEntries.get(i);
100:
101: int start_pc = entry.mStart.getLocation();
102:
103: if (start_pc < 0 || start_pc > 65535) {
104: throw new IllegalStateException(
105: "Value for line number table entry start PC out of "
106: + "valid range: " + start_pc);
107: }
108:
109: dout.writeShort(start_pc);
110: dout.writeShort(entry.mLineNumber);
111: }
112: }
113:
114: private void clean() {
115: if (!mClean) {
116: mClean = true;
117:
118: // Clean things up by removing multiple mappings of the same
119: // start_pc to line numbers. Only keep the last one.
120: // This has to be performed now because the Labels should have
121: // a pc location, but before they did not. Since entries must be
122: // sorted ascending by start_pc, use a sorted set.
123:
124: Set reduced = new TreeSet();
125: for (int i = mEntries.size(); --i >= 0;) {
126: reduced.add(mEntries.get(i));
127: }
128:
129: mEntries = new ArrayList(reduced);
130: }
131: }
132:
133: private static class Entry implements Comparable {
134: public final Location mStart;
135: public final int mLineNumber;
136:
137: public Entry(Location start, int line_number) {
138: mStart = start;
139: mLineNumber = line_number;
140: }
141:
142: public int compareTo(Object other) {
143: int this Loc = mStart.getLocation();
144: int thatLoc = ((Entry) other).mStart.getLocation();
145:
146: if (this Loc < thatLoc) {
147: return -1;
148: } else if (this Loc > thatLoc) {
149: return 1;
150: } else {
151: return 0;
152: }
153: }
154:
155: public boolean equals(Object other) {
156: if (other instanceof Entry) {
157: return mStart.getLocation() == ((Entry) other).mStart
158: .getLocation();
159: }
160: return false;
161: }
162:
163: public String toString() {
164: return "start_pc=" + mStart.getLocation() + " => "
165: + "line_number=" + mLineNumber;
166: }
167: }
168: }
|