org.jboss.jandex.IndexWriterV1 Maven / Gradle / Ivy
/*
* JBoss, Home of Professional Open Source.
* Copyright 2013 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* 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 org.jboss.jandex;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
/**
* Writes a Jandex index file to a stream. The write process is somewhat more
* expensive to allow for fast reads and a compact size. For more information on
* the index content, see the documentation on {@link org.jboss.jandex.Indexer}.
*
*
* The IndexWriter operates on standard output streams, and also provides
* suitable buffering.
*
*
* Thread-Safety
*
* IndexWriter is not thread-safe and can not be shared between concurrent
* threads.
*
* @see org.jboss.jandex.Indexer
* @see org.jboss.jandex.Index
* @author Jason T. Greene
*
*/
final class IndexWriterV1 extends IndexWriterImpl {
// babelfish (no h)
private static final int MAGIC = 0xBABE1F15;
static final int MIN_VERSION = 1;
static final int MAX_VERSION = 3;
private static final byte FIELD_TAG = 1;
private static final byte METHOD_TAG = 2;
private static final byte METHOD_PARAMATER_TAG = 3;
private static final byte CLASS_TAG = 4;
private static final int AVALUE_BYTE = 1;
private static final int AVALUE_SHORT = 2;
private static final int AVALUE_INT = 3;
private static final int AVALUE_CHAR = 4;
private static final int AVALUE_FLOAT = 5;
private static final int AVALUE_DOUBLE = 6;
private static final int AVALUE_LONG = 7;
private static final int AVALUE_BOOLEAN = 8;
private static final int AVALUE_STRING = 9;
private static final int AVALUE_CLASS = 10;
private static final int AVALUE_ENUM = 11;
private static final int AVALUE_ARRAY = 12;
private static final int AVALUE_NESTED = 13;
private final OutputStream out;
private StrongInternPool pool;
StrongInternPool.Index poolIndex;
private TreeMap classTable;
/**
* Constructs an IndexWriter using the specified stream
*
* @param out a stream to write an index to
*/
IndexWriterV1(OutputStream out) {
this.out = out;
}
/**
* Writes the specified index to the associated output stream. This may be called multiple times in order
* to write multiple indexes.
*
* @param index the index to write to the stream
* @param version the index file version
* @return the number of bytes written to the stream
* @throws java.io.IOException if any i/o error occurs
*/
int write(Index index, int version) throws IOException {
if (version < MIN_VERSION || version > MAX_VERSION) {
throw new UnsupportedVersion("Version: " + version);
}
PackedDataOutputStream stream = new PackedDataOutputStream(new BufferedOutputStream(out));
stream.writeInt(MAGIC);
stream.writeByte(version);
buildTables(index);
writeClassTable(stream);
writeStringTable(stream);
writeClasses(stream, index, version);
stream.flush();
return stream.size();
}
private void writeStringTable(PackedDataOutputStream stream) throws IOException {
stream.writePackedU32(pool.size());
Iterator iter = pool.iterator();
while (iter.hasNext()) {
String string = iter.next();
stream.writeUTF(string);
}
}
private void writeClassTable(PackedDataOutputStream stream) throws IOException {
stream.writePackedU32(classTable.size());
// Zero is reserved for null
int pos = 1;
for (Entry entry : classTable.entrySet()) {
entry.setValue(pos++);
DotName name = entry.getKey();
assert name.isComponentized();
int nameDepth = 0;
for (DotName prefix = name.prefix(); prefix != null; prefix = prefix.prefix())
nameDepth++;
stream.writePackedU32(nameDepth);
stream.writeUTF(name.local());
}
}
private int positionOf(String string) {
// V1 format does not use 0 as a null placeholder
int i = poolIndex.positionOf(string) - 1;
if (i < 0)
throw new IllegalStateException();
return i;
}
private int positionOf(DotName className) {
className = downgradeName(className);
Integer i = classTable.get(className);
if (i == null)
throw new IllegalStateException("Class not found in class table:" + className);
return i.intValue();
}
private void writeClasses(PackedDataOutputStream stream, Index index, int version) throws IOException {
Collection classes = index.getKnownClasses();
stream.writePackedU32(classes.size());
for (ClassInfo clazz: classes) {
stream.writePackedU32(positionOf(clazz.name()));
stream.writePackedU32(clazz.superName() == null ? 0 : positionOf(clazz.superName()));
stream.writeShort(clazz.flags());
// hasNoArgsConstructor supported since version 3
if (version >= 3) {
stream.writeBoolean(clazz.hasNoArgsConstructor());
}
DotName[] interfaces = clazz.interfaces();
stream.writePackedU32(interfaces.length);
for (DotName intf: interfaces)
stream.writePackedU32(positionOf(intf));
Set>> entrySet = clazz.annotations().entrySet();
stream.writePackedU32(entrySet.size());
for (Entry> entry : entrySet) {
stream.writePackedU32(positionOf(entry.getKey()));
List instances = entry.getValue();
stream.writePackedU32(instances.size());
for (AnnotationInstance instance : instances) {
AnnotationTarget target = instance.target();
if (target instanceof FieldInfo) {
FieldInfo field = (FieldInfo) target;
stream.writeByte(FIELD_TAG);
stream.writePackedU32(positionOf(field.name()));
writeType(stream, field.type());
stream.writeShort(field.flags());
} else if (target instanceof MethodInfo) {
MethodInfo method = (MethodInfo) target;
stream.writeByte(METHOD_TAG);
stream.writePackedU32(positionOf(method.name()));
stream.writePackedU32(method.args().length);
for (int i = 0; i < method.args().length; i ++) {
writeType(stream, method.args()[i]);
}
writeType(stream, method.returnType());
stream.writeShort(method.flags());
} else if (target instanceof MethodParameterInfo) {
MethodParameterInfo param = (MethodParameterInfo) target;
MethodInfo method = param.method();
stream.writeByte(METHOD_PARAMATER_TAG);
stream.writePackedU32(positionOf(method.name()));
stream.writePackedU32(method.args().length);
for (int i = 0; i < method.args().length; i ++) {
writeType(stream, method.args()[i]);
}
writeType(stream, method.returnType());
stream.writeShort(method.flags());
stream.writePackedU32(param.position());
} else if (target instanceof ClassInfo) {
stream.writeByte(CLASS_TAG);
} else throw new IllegalStateException("Unknown target");
Collection values = instance.values();
writeAnnotationValues(stream, values);
}
}
}
}
private void writeAnnotationValues(PackedDataOutputStream stream, Collection values) throws IOException {
stream.writePackedU32(values.size());
for (AnnotationValue value : values) {
writeAnnotationValue(stream, value);
}
}
private void writeAnnotationValue(PackedDataOutputStream stream, AnnotationValue value) throws IOException {
stream.writePackedU32(positionOf(value.name()));
if (value instanceof AnnotationValue.ByteValue) {
stream.writeByte(AVALUE_BYTE);
stream.writeByte(value.asByte() & 0xFF);
} else if (value instanceof AnnotationValue.ShortValue) {
stream.writeByte(AVALUE_SHORT);
stream.writePackedU32(value.asShort() & 0xFFFF);
} else if (value instanceof AnnotationValue.IntegerValue) {
stream.writeByte(AVALUE_INT);
stream.writePackedU32(value.asInt());
} else if (value instanceof AnnotationValue.CharacterValue) {
stream.writeByte(AVALUE_CHAR);
stream.writePackedU32(value.asChar());
} else if (value instanceof AnnotationValue.FloatValue) {
stream.writeByte(AVALUE_FLOAT);
stream.writeFloat(value.asFloat());
} else if (value instanceof AnnotationValue.DoubleValue) {
stream.writeByte(AVALUE_DOUBLE);
stream.writeDouble(value.asDouble());
} else if (value instanceof AnnotationValue.LongValue) {
stream.writeByte(AVALUE_LONG);
stream.writeLong(value.asLong());
} else if (value instanceof AnnotationValue.BooleanValue) {
stream.writeByte(AVALUE_BOOLEAN);
stream.writeBoolean(value.asBoolean());
} else if (value instanceof AnnotationValue.StringValue) {
stream.writeByte(AVALUE_STRING);
stream.writePackedU32(positionOf(value.asString()));
} else if (value instanceof AnnotationValue.ClassValue) {
stream.writeByte(AVALUE_CLASS);
writeType(stream, value.asClass());
} else if (value instanceof AnnotationValue.EnumValue) {
stream.writeByte(AVALUE_ENUM);
stream.writePackedU32(positionOf(value.asEnumType()));
stream.writePackedU32(positionOf(value.asEnum()));
} else if (value instanceof AnnotationValue.ArrayValue) {
AnnotationValue[] array = value.asArray();
int length = array.length;
stream.writeByte(AVALUE_ARRAY);
stream.writePackedU32(length);
for (int i = 0; i < length; i++) {
writeAnnotationValue(stream, array[i]);
}
} else if (value instanceof AnnotationValue.NestedAnnotation) {
AnnotationInstance instance = value.asNested();
Collection values = instance.values();
stream.writeByte(AVALUE_NESTED);
stream.writePackedU32(positionOf(instance.name()));
writeAnnotationValues(stream, values);
}
}
private void writeType(PackedDataOutputStream stream, Type type) throws IOException {
stream.writeByte(type.kind().ordinal());
stream.writePackedU32(positionOf(type.name()));
}
private void buildTables(Index index) {
pool = new StrongInternPool();
classTable = new TreeMap();
// Build the pool for all strings
for (ClassInfo clazz: index.getKnownClasses()) {
addClassName(clazz.name());
if (clazz.superName() != null)
addClassName(clazz.superName());
for (DotName intf: clazz.interfaces())
addClassName(intf);
for (Entry> entry : clazz.annotations().entrySet()) {
addClassName(entry.getKey());
for (AnnotationInstance instance: entry.getValue()) {
AnnotationTarget target = instance.target();
if (target instanceof FieldInfo) {
FieldInfo field = (FieldInfo) target;
intern(field.name());
addClassName(field.type().name());
} else if (target instanceof MethodInfo) {
MethodInfo method = (MethodInfo) target;
intern(method.name());
for (Type type : method.args())
addClassName(type.name());
addClassName(method.returnType().name());
}
else if (target instanceof MethodParameterInfo) {
MethodParameterInfo param = (MethodParameterInfo) target;
intern(param.method().name());
for (Type type : param.method().args())
addClassName(type.name());
addClassName(param.method().returnType().name());
}
for (AnnotationValue value : instance.values())
buildAValueEntries(index, value);
}
}
}
poolIndex = pool.index();
}
private void buildAValueEntries(Index index, AnnotationValue value) {
intern(value.name());
if (value instanceof AnnotationValue.StringValue) {
intern(value.asString());
} else if (value instanceof AnnotationValue.ClassValue) {
addClassName(value.asClass().name());
} else if (value instanceof AnnotationValue.EnumValue) {
addClassName(value.asEnumType());
intern(value.asEnum());
} else if (value instanceof AnnotationValue.ArrayValue) {
for (AnnotationValue entry : value.asArray())
buildAValueEntries(index, entry);
} else if (value instanceof AnnotationValue.NestedAnnotation) {
AnnotationInstance instance = value.asNested();
Collection values = instance.values();
addClassName(instance.name());
for (AnnotationValue entry : values) {
buildAValueEntries(index, entry);
}
}
}
private String intern(String name) {
return pool.intern(name);
}
private void addClassName(DotName name) {
name = downgradeName(name);
if (! classTable.containsKey(name))
classTable.put(name, null);
DotName prefix = name.prefix();
if (prefix != null)
addClassName(prefix);
}
private DotName downgradeName(DotName name) {
DotName n = name;
StringBuilder builder = null;
while (n.isInner()) {
if (builder == null) {
builder = new StringBuilder();
}
builder.insert(0, n.local()).insert(0, '$');
if (! n.prefix().isInner()) {
builder.insert(0, n.prefix().local());
name = new DotName(n.prefix().prefix(), builder.toString(), true, false);
}
n = n.prefix();
}
return name;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy