/*
* Copyright 2013 eXo Platform SAS
*
* 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 juzu.impl.metamodel;
import juzu.impl.common.Name;
import juzu.impl.compiler.BaseProcessor;
import juzu.impl.compiler.MessageCode;
import juzu.impl.compiler.ProcessingContext;
import juzu.impl.common.Logger;
import juzu.impl.common.Tools;
import javax.annotation.Generated;
import javax.annotation.processing.Completion;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.tools.FileObject;
import javax.tools.StandardLocation;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Set;
/** @author <a href="mailto:julien.viet@exoplatform.com">Julien Viet</a> */
public abstract class MetaModelProcessor<P extends MetaModelPlugin<M, P>, M extends MetaModel<P, M>> extends BaseProcessor {
/** . */
public static final MessageCode ANNOTATION_UNSUPPORTED = new MessageCode("ANNOTATION_UNSUPPORTED", "The annotation of this element cannot be supported");
/** . */
private MetaModelState<P, M> state;
/** . */
private int index;
/** . */
private final Logger log = BaseProcessor.getLogger(getClass());
/** . */
private HashSet<String> supportedAnnotations;
@Override
protected void doInit(ProcessingContext context) {
log.info("Using processing env " + context.getClass().getName());
// Try to get state or create new one
if (state == null) {
InputStream in = null;
try {
FileObject file = getContext().getResource(StandardLocation.SOURCE_OUTPUT, "juzu", "metamodel.ser");
in = file.openInputStream();
ObjectInputStream ois = new ObjectInputStream(in);
state = (MetaModelState<P, M>)ois.readObject();
log.info("Loaded model from " + file.toUri());
}
catch (Exception e) {
log.info("Created new meta model");
MetaModelState<P, M> metaModel = new MetaModelState<P, M>(getPluginType(), createMetaModel());
metaModel.init(getContext());
state = metaModel;
}
finally {
Tools.safeClose(in);
}
}
//
HashSet<String> supportedAnnotations = new HashSet<String>();
for (Class<?> supportedAnnotation : state.context.getSupportedAnnotations()) {
supportedAnnotations.add(supportedAnnotation.getName());
}
//
this.supportedAnnotations = supportedAnnotations;
this.index = 0;
}
protected abstract Class<P> getPluginType();
protected abstract M createMetaModel();
@Override
protected void doProcess(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
if (!roundEnv.errorRaised()) {
if (roundEnv.processingOver()) {
log.info("APT processing over");
//
log.info("Passivating model");
state.metaModel.prePassivate();
// Passivate model
ObjectOutputStream out = null;
try {
FileObject file = getContext().createResource(StandardLocation.SOURCE_OUTPUT, "juzu", "metamodel.ser");
out = new ObjectOutputStream(file.openOutputStream());
out.writeObject(state);
state = null;
}
catch (Exception e) {
e.printStackTrace();
log.info("Could not passivate model ", e);
}
finally {
Tools.safeClose(out);
}
}
else {
log.info("Starting APT round #" + index);
//
if (index == 0) {
log.info("Activating model");
state.metaModel.postActivate(getContext());
}
//
LinkedHashMap<AnnotationKey, AnnotationState> updates = new LinkedHashMap<AnnotationKey, AnnotationState>();
for (TypeElement annotationElt : annotations) {
if (supportedAnnotations.contains(annotationElt.getQualifiedName().toString())) {
log.info("Processing elements for annotation for " + annotationElt.getQualifiedName());
for (Element annotatedElt : roundEnv.getElementsAnnotatedWith(annotationElt)) {
if (annotatedElt.getAnnotation(Generated.class) == null) {
log.info("Processing element " + annotatedElt);
for (AnnotationMirror annotationMirror : annotatedElt.getAnnotationMirrors()) {
if (annotationMirror.getAnnotationType().asElement().equals(annotationElt)) {
AnnotationKey key = new AnnotationKey(annotatedElt, annotationMirror);
AnnotationState state = AnnotationState.create(annotationMirror);
updates.put(key, state);
}
}
}
}
}
}
//
log.info("Process annotations");
state.context.processAnnotations(updates.entrySet());
//
log.info("Post processing model");
state.metaModel.postProcessAnnotations();
//
log.info("Process events");
state.metaModel.processEvents();
//
log.info("Post process events");
state.metaModel.postProcessEvents();
//
log.info("Ending APT round #" + index++);
}
}
}
@Override
public Iterable<? extends Completion> getCompletions(Element element, AnnotationMirror annotation, ExecutableElement member, String userText) {
Iterable<? extends Completion> completions;
// For now we don't provide completion when element is absent
if (element != null) {
// Get state (but we won't save it)
log.info("Activating model");
state.metaModel.postActivate(getContext());
AnnotationKey annotationKey = new AnnotationKey(element, Name.parse(((TypeElement)annotation.getAnnotationType().asElement()).getQualifiedName().toString()));
AnnotationState annotationState = AnnotationState.create(annotation);
completions = state.context.getCompletions(annotationKey, annotationState, member.getSimpleName().toString(), userText);
} else {
completions = Collections.emptyList();
}
return completions != null ? completions : Collections.<Completion>emptyList();
}
}