
org.scijava.annotations.AbstractIndexWriter Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of scijava-common Show documentation
Show all versions of scijava-common Show documentation
SciJava Common is a shared library for SciJava software. It provides a plugin framework, with an extensible mechanism for service discovery, backed by its own annotation processor, so that plugins can be loaded dynamically. It is used by both ImageJ and SCIFIO.
/*
* #%L
* SciJava Common shared library for SciJava software.
* %%
* Copyright (C) 2009 - 2016 Board of Regents of the University of
* Wisconsin-Madison, Broad Institute of MIT and Harvard, and Max Planck
* Institute of Molecular Cell Biology and Genetics.
* %%
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
* #L%
*/
package org.scijava.annotations;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentSkipListMap;
import javax.lang.model.element.AnnotationValue;
/**
* Writes annotations as JSON-formatted files.
*
* The file names are the names of the annotations, and the serialized data
* describe the class which was annotated together with the specific annotation
* fields.
*
*
* @author Johannes Schindelin
*/
public abstract class AbstractIndexWriter {
private final Map> map =
new ConcurrentSkipListMap>();
protected synchronized boolean foundAnnotations() {
return !map.isEmpty();
}
protected synchronized void add(final Map annotationValues,
final String annotationName, final String className)
{
Map list = map.get(annotationName);
if (list == null) {
list = new LinkedHashMap();
map.put(annotationName, list);
}
final Map o = new TreeMap();
o.put("class", className);
o.put("values", annotationValues);
list.put(className, o);
}
public interface StreamFactory {
InputStream openInput(String annotationName) throws IOException;
OutputStream openOutput(String annotationName) throws IOException;
boolean isClassObsolete(String className);
}
protected synchronized void write(final StreamFactory factory)
throws IOException
{
for (Entry> entry : map.entrySet()) {
final String annotationName = entry.getKey();
merge(annotationName, factory);
final PrintStream out =
new PrintStream(factory.openOutput(annotationName));
for (Object o : entry.getValue().values()) {
writeObject(out, adapt(o));
}
out.close();
}
map.clear();
}
/**
* Merges an existing annotation index into the currently-generated one.
*
* This method is used to read previously-indexed annotations and reconcile
* them with the newly-generated ones just.
*
*
* @param annotationName the name of the annotation for which the index
* contains the annotated classes
* @param factory the factory to generate input and output streams given an
* annotation name
* @throws IOException
*/
protected synchronized void merge(final String annotationName,
final StreamFactory factory) throws IOException
{
final InputStream in = factory.openInput(annotationName);
if (in == null) {
return;
}
Map m = map.get(annotationName);
if (m == null) {
m = new LinkedHashMap();
map.put(annotationName, m);
}
/*
* To determine whether the index needs to be written out,
* we need to keep track of changed entries.
*/
int changedCount = m.size();
boolean hasObsoletes = false;
final IndexReader reader =
new IndexReader(in, annotationName + " from " + in);
try {
for (;;) {
@SuppressWarnings("unchecked")
final Map entry = (Map) reader.next();
if (entry == null) {
break;
}
final String className = (String) entry.get("class");
if (factory.isClassObsolete(className)) {
hasObsoletes = true;
}
else if (m.containsKey(className)) {
if (!hasObsoletes && entry.equals(m.get(className))) {
changedCount--;
}
}
else {
m.put(className, entry);
}
}
}
finally {
reader.close();
}
// if this annotation index is unchanged, no need to write it out again
if (changedCount == 0 && !hasObsoletes) {
map.remove(annotationName);
}
}
protected Object adapt(final Object o) {
if (o instanceof Annotation) {
return adapt((Annotation) o);
}
else if (o instanceof AnnotationValue) {
return adapt(((AnnotationValue) o).getValue());
}
else if (o instanceof Enum) {
return adapt((Enum>) o);
}
return o;
}
protected Map adapt(A annotation) {
Map result = new TreeMap();
for (Method method : annotation.annotationType().getMethods())
try {
if (method.getDeclaringClass() == annotation.annotationType()) {
result.put(method.getName(), adapt(method.invoke(annotation)));
}
}
catch (IllegalArgumentException e) {
e.printStackTrace();
}
catch (IllegalAccessException e) {
e.printStackTrace();
}
catch (InvocationTargetException e) {
e.printStackTrace();
}
return result;
}
private static Map adapt(Enum> e) {
Map result = new TreeMap();
result.put("enum", e.getClass().getName());
result.put("value", e.name());
return result;
}
private void writeObject(final PrintStream out, final Object o)
throws IOException
{
if (o == null) {
out.print("null");
}
else if (o instanceof Boolean) {
out.print((Boolean) o ? "true" : "false");
}
else if (o instanceof Byte) {
out.print((byte) (Byte) o);
}
else if (o instanceof Short) {
out.print((short) (Short) o);
}
else if (o instanceof Integer) {
out.print((int) (Integer) o);
}
else if (o instanceof Long) {
out.print((long) (Long) o);
}
else if (o instanceof Float) {
out.print((float) (Float) o);
}
else if (o instanceof Double) {
out.print((double) (Double) o);
}
else if (o instanceof Character) {
writeString(out, "" + o);
}
else if (o instanceof String) {
writeString(out, (String) o);
}
else if (o instanceof Class) {
writeString(out, ((Class>) o).getName());
}
else if (o instanceof List) {
writeArray(out, (List>) o);
}
else if (o.getClass().isArray()) {
writeArray(out, o);
}
else if (o instanceof Map) {
writeMap(out, (Map, ?>) o);
}
else {
throw new IOException("Cannot handle object of type " + o.getClass());
}
}
protected void writeMap(final PrintStream out, final Object... pairs)
throws IOException
{
if ((pairs.length % 2) != 0) {
throw new IOException("Key without value!");
}
out.write('{');
for (int i = 0; i < pairs.length; i += 2) {
if (i > 0) {
out.write(',');
}
writeString(out, (String) pairs[i]);
out.write(':');
writeObject(out, pairs[i + 1]);
}
out.write('}');
}
private void writeMap(final PrintStream out, final Map, ?> m)
throws IOException
{
out.write('{');
boolean first = true;
for (Map.Entry, ?> entry : m.entrySet()) {
if (first) {
first = false;
}
else {
out.write(',');
}
writeString(out, entry.getKey().toString());
out.write(':');
writeObject(out, entry.getValue());
}
out.write('}');
}
private void writeArray(final PrintStream out, final List> list)
throws IOException
{
out.write('[');
boolean first = true;
for (Object o : list) {
if (first) {
first = false;
}
else {
out.write(',');
}
o = adapt(o);
writeObject(out, o);
}
out.write(']');
}
private void writeArray(final PrintStream out, final Object o)
throws IOException
{
out.write('[');
int length = Array.getLength(o);
for (int i = 0; i < length; i++) {
if (i > 0) {
out.write(',');
}
writeObject(out, adapt(Array.get(o, i)));
}
out.write(']');
}
private void writeString(final PrintStream out, final String string) {
out.write('"');
for (char c : string.toCharArray()) {
if (c == '"' || c == '\\') {
out.write('\\');
out.write(c);
}
else if (c >= ' ' && c <= 0x7f) {
out.write(c);
}
else {
String hex = Integer.toHexString(c);
out.print("\\u");
if (hex.length() < 4) {
out.print("0000".substring(hex.length()));
}
out.print(hex);
}
}
out.write('"');
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy