Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 2d1ed972d22bf83fd4cc1f71458edf6db9da4cf2 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
/*******************************************************************************
 * Copyright (c) 2006, 2011 BEA Systems, Inc. 
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *    wharley@bea.com - initial API and implementation
 *    
 *******************************************************************************/

package org.eclipse.jdt.internal.compiler.apt.dispatch;

import java.util.Iterator;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.annotation.processing.Processor;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.TypeElement;

/**
 * Cached information associated with a {@link Processor} in the context
 * of annotation processor dispatch.
 * <p>
 * This class supports inclusion in a collection by implementing
 * equals() and hashCode().  Its concept of identity is based on
 * the class object of the Processor that it wraps; so, for instance,
 * it is not possible to have a Set that contains more than one
 * instance of a particular Processor class.  In fact, it is possible
 * to have more than one instance of a Processor if there are multiple
 * build threads, but within the context of a particular dispatch
 * manager, there will only be one of any given Processor class.
 */
public class ProcessorInfo {
	final Processor _processor;
	final Set<String> _supportedOptions;
	final SourceVersion _supportedSourceVersion;

	private final Pattern _supportedAnnotationTypesPattern;
	private final boolean _supportsStar;
	private boolean _hasBeenCalled;

	/**
	 * Create a ProcessorInfo wrapping a particular Processor. The Processor must already have been
	 * initialized (that is,
	 * {@link Processor#init(javax.annotation.processing.ProcessingEnvironment)} must already have
	 * been called). Its getSupportedXXX() methods will be called and the results will be cached.
	 */
	public ProcessorInfo(Processor p) 
	{
		_processor = p;
		_hasBeenCalled = false;
		_supportedSourceVersion = p.getSupportedSourceVersion();
		_supportedOptions = p.getSupportedOptions();
		Set<String> supportedAnnotationTypes = p.getSupportedAnnotationTypes();
		
		boolean supportsStar = false;
		if (null != supportedAnnotationTypes && !supportedAnnotationTypes.isEmpty()) {
			StringBuilder regex = new StringBuilder();
			Iterator<String> iName = supportedAnnotationTypes.iterator();
			while (true) {
				String name = iName.next();
				supportsStar |= "*".equals(name);  //$NON-NLS-1$
				String escapedName1 = name.replace(".", "\\."); //$NON-NLS-1$ //$NON-NLS-2$
				String escapedName2 = escapedName1.replace("*", ".*"); //$NON-NLS-1$ //$NON-NLS-2$
				regex.append(escapedName2);
				if (!iName.hasNext()) {
					break;
				}
				regex.append('|');
			}
			_supportedAnnotationTypesPattern = Pattern.compile(regex.toString());
		}
		else {
			_supportedAnnotationTypesPattern = null;
		}
		_supportsStar = supportsStar;
	}
	
	/**
	 * Compute the subset of <code>annotations</code> that are described by <code>annotationTypes</code>,
	 * and determine whether the processor should be called.  A processor will be called if it has
	 * any annotations to process, or if it supports "*", or if it was called in a previous round.
	 * If the return value of this method is true once for a given processor, then it will always be true on
	 * subsequent calls.
	 * 
	 * @param annotations a set of annotation types
	 * @param result an empty modifiable set, which upon return will contain a subset of <code>annotations</code>, which may be empty but will not be null.
	 * @return true if the processor should be called on this round.
	 */
	public boolean computeSupportedAnnotations(Set<TypeElement> annotations, Set<TypeElement> result)
	{
		if (null != annotations && !annotations.isEmpty() && null != _supportedAnnotationTypesPattern) {
			for (TypeElement annotation : annotations) {
				Matcher matcher = _supportedAnnotationTypesPattern.matcher(annotation.getQualifiedName().toString());
				if (matcher.matches()) {
					result.add(annotation);
				}
			}
		}
		boolean call = _hasBeenCalled || _supportsStar || !result.isEmpty();
		_hasBeenCalled |= call;
		return call;
	}

	/**
	 * @return true if the processor included "*" among its list of supported annotations.
	 */
	public boolean supportsStar()
	{
		return _supportsStar;
	}
	
	/**
	 * Must be called at the beginning of a build to ensure that no information is
	 * carried over from the previous build.  In particular, processors are
	 * required to be called on every round after the round in which they are
	 * first called; this method resets the "has been called" flag.
	 */
	public void reset()
	{
		_hasBeenCalled = false;
	}

	@Override
	public int hashCode() {
		return _processor.getClass().hashCode();
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		final ProcessorInfo other = (ProcessorInfo) obj;
		if (!_processor.getClass().equals(other._processor.getClass()))
			return false;
		return true;
	}

	@Override
	public String toString()
	{
		return _processor.getClass().getName();
	}
	
	/**
	 * @return a string representing the set of supported annotation types, in a format
	 * suitable for debugging.  The format is unspecified and subject to change.
	 */
	public String getSupportedAnnotationTypesAsString()
	{
		StringBuilder sb = new StringBuilder();
		sb.append('[');
		Iterator<String> iAnnots = _processor.getSupportedAnnotationTypes().iterator(); 
		boolean hasNext = iAnnots.hasNext();
		while (hasNext) {
			sb.append(iAnnots.next());
			hasNext = iAnnots.hasNext();
			if (hasNext) {
				sb.append(',');
			}
		}
		sb.append(']');
		return sb.toString();
	}
}

Back to the top