/******************************************************************************* * Copyright (c) 2006, 2007 BEA Systems, Inc. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * wharley@bea.com - initial API and implementation * *******************************************************************************/ package org.eclipse.jdt.internal.compiler.apt.dispatch; import java.io.PrintWriter; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import javax.annotation.processing.RoundEnvironment; import javax.lang.model.element.Element; import javax.lang.model.element.TypeElement; /** * Manages context during a single round of annotation processing. */ public class RoundDispatcher { private final Set<TypeElement> _unclaimedAnnotations; private final RoundEnvironment _roundEnv; private final IProcessorProvider _provider; private boolean _searchForStar = false; private final PrintWriter _traceProcessorInfo; private final PrintWriter _traceRounds; /** * Processors discovered so far. This list may grow during the * course of a round, as additional processors are discovered. */ private final List<ProcessorInfo> _processors; /** * @param rootAnnotations a possibly empty but non-null set of annotations on the * root compilation units of this round. A local copy of the set will be made, to * avoid modifying the set passed in. * @param traceProcessorInfo a PrintWriter that processor trace output will be sent * to, or null if tracing is not desired. * @param traceRounds */ public RoundDispatcher( IProcessorProvider provider, RoundEnvironment env, Set<TypeElement> rootAnnotations, PrintWriter traceProcessorInfo, PrintWriter traceRounds) { _provider = provider; _processors = provider.getDiscoveredProcessors(); _roundEnv = env; _unclaimedAnnotations = new HashSet<TypeElement>(rootAnnotations); _traceProcessorInfo = traceProcessorInfo; _traceRounds = traceRounds; } /** * Handle a complete round, dispatching to all appropriate processors. */ public void round() { if (null != _traceRounds) { StringBuilder sbElements = new StringBuilder(); sbElements.append("\tinput files: {"); //$NON-NLS-1$ Iterator<? extends Element> iElements = _roundEnv.getRootElements().iterator(); boolean hasNext = iElements.hasNext(); while (hasNext) { sbElements.append(iElements.next()); hasNext = iElements.hasNext(); if (hasNext) { sbElements.append(','); } } sbElements.append('}'); _traceRounds.println(sbElements.toString()); StringBuilder sbAnnots = new StringBuilder(); sbAnnots.append("\tannotations: ["); //$NON-NLS-1$ Iterator<TypeElement> iAnnots = _unclaimedAnnotations.iterator(); hasNext = iAnnots.hasNext(); while (hasNext) { sbAnnots.append(iAnnots.next()); hasNext = iAnnots.hasNext(); if (hasNext) { sbAnnots.append(','); } } sbAnnots.append(']'); _traceRounds.println(sbAnnots.toString()); _traceRounds.println("\tlast round: " + _roundEnv.processingOver()); //$NON-NLS-1$ } // If there are no root annotations, try to find a processor that claims "*" _searchForStar = _unclaimedAnnotations.isEmpty(); // Iterate over all the already-found processors, giving each one a chance at the unclaimed // annotations. If a processor is called at all, it is called on every subsequent round // including the final round, but it may be called with an empty set of annotations. for (ProcessorInfo pi : _processors) { handleProcessor(pi); } // If there are any unclaimed annotations, or if there were no root annotations and // we have not yet run into a processor that claimed "*", continue discovery. while (_searchForStar || !_unclaimedAnnotations.isEmpty()) { ProcessorInfo pi = _provider.discoverNextProcessor(); if (null == pi) { // There are no more processors to be discovered. break; } handleProcessor(pi); } // TODO: If !unclaimedAnnos.isEmpty(), issue a warning. } /** * Evaluate a single processor. Depending on the unclaimed annotations, * the annotations this processor supports, and whether it has already been * called in a previous round, possibly call its process() method. */ private void handleProcessor(ProcessorInfo pi) { try { Set<TypeElement> annotationsToProcess = new HashSet<TypeElement>(); boolean shouldCall = pi.computeSupportedAnnotations( _unclaimedAnnotations, annotationsToProcess); if (shouldCall) { boolean claimed = pi._processor.process(annotationsToProcess, _roundEnv); if (null != _traceProcessorInfo && !_roundEnv.processingOver()) { StringBuilder sb = new StringBuilder(); sb.append("Processor "); //$NON-NLS-1$ sb.append(pi._processor.getClass().getName()); sb.append(" matches ["); //$NON-NLS-1$ Iterator<TypeElement> i = annotationsToProcess.iterator(); boolean hasNext = i.hasNext(); while (hasNext) { sb.append(i.next()); hasNext = i.hasNext(); if (hasNext) { sb.append(' '); } } sb.append("] and returns "); //$NON-NLS-1$ sb.append(claimed); _traceProcessorInfo.println(sb.toString()); } if (claimed) { // The processor claimed its annotations. _unclaimedAnnotations.removeAll(annotationsToProcess); if (pi.supportsStar()) { _searchForStar = false; } } } } catch (Exception e) { // If a processor throws an exception (as opposed to reporting an error), // report it and abort compilation by throwing AbortCompilation. _provider.reportProcessorException(pi._processor, e); } } }