Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 095bd167588d5add709e5670e7fc0fe5fcc2e337 (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
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
/*******************************************************************************
 * Copyright (c) 2005, 2018 BEA Systems, Inc. and others.
 *
 * 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:
 *    mkaufman@bea.com - initial API and implementation
 *    
 *******************************************************************************/

package org.eclipse.jdt.apt.core.internal.generatedfile;

import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.IResourceDeltaVisitor;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jdt.apt.core.internal.AptPlugin;
import org.eclipse.jdt.apt.core.internal.AptProject;
import org.eclipse.jdt.apt.core.util.AptConfig;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;

/**
 * A jdt.core pre-process resource change listener that manages generated resources.
 * <p>
 * 
 * Note that this is both a pre-build listener and a post-change listener, 
 * because there is a bug in the resource change event notification in the platform:
 * sometimes they fail to send out deletion notifications for files in pre-build,
 * but they do send them out in post-change. 
 */
public class GeneratedResourceChangeListener implements IResourceChangeListener 
{
	// Synchronized collection, as post-change notifications could come in 
	// simultaneously. Note that pre-build will not though, as it holds the
	// workspace lock
	private final Set<IResource> deletedResources = 
		Collections.synchronizedSet(new HashSet<IResource>());
	
	public GeneratedResourceChangeListener(){}
	
	@Override
	public void resourceChanged(IResourceChangeEvent event) 
	{
		if ( event.getType() == IResourceChangeEvent.PRE_CLOSE )
		{
			IProject p = (IProject)event.getResource();
			if( AptPlugin.DEBUG_GFM ) 
				AptPlugin.trace(
						"generated resource change listener got a pre-close event: project = " + p.getName()); //$NON-NLS-1$
			IJavaProject jp = JavaCore.create(p);
			AptPlugin.getAptProject(jp).projectClosed();
		}
		else if ( event.getType() == IResourceChangeEvent.PRE_DELETE )
		{
			// TODO:  need to update projectDeleted() to delete the generated_src folder
			// in an async thread.  The resource tree is locked here.
			IProject p = (IProject)event.getResource();
			if( AptPlugin.DEBUG_GFM ) 
				AptPlugin.trace(
						"generated resource change listener got a pre-delete event: project = " + p.getName()); //$NON-NLS-1$
			IJavaProject jp = JavaCore.create(p);
			AptPlugin.getAptProject(jp).projectDeleted();
			AptPlugin.deleteAptProject(jp);
		}
		else if ( event.getType() == IResourceChangeEvent.PRE_BUILD )
		{
			try
			{ 
				if( AptPlugin.DEBUG_GFM ) 
					AptPlugin.trace("generated resource change listener got a pre-build event"); //$NON-NLS-1$
				
				final PreBuildVisitor pbv = new PreBuildVisitor();
				
				// First we need to handle previously deleted resources (from the post-change event),
				// because we could not perform file i/o during that event
				for (IResource resource : deletedResources) {
					pbv.handleDeletion(resource);
				}
				
				event.getDelta().accept( pbv );
				addGeneratedSrcFolderTo(pbv.getProjectsThatNeedGenSrcFolder(), false);
				addGeneratedSrcFolderTo(pbv.getProjectsThatNeedGenTestSrcFolder(), true);
				
				// Now clear the set of deleted resources,
				// as we don't want to re-handle them
				deletedResources.clear();
			}
			catch ( CoreException ce )
			{
				AptPlugin.log(ce, "Error during pre-build resource change"); //$NON-NLS-1$
			}
		}
		else if (event.getType() == IResourceChangeEvent.POST_CHANGE) {
			if( AptPlugin.DEBUG_GFM ) 
				AptPlugin.trace(
						"generated resource change listener got a post-change event"); //$NON-NLS-1$
			PostChangeVisitor pcv = new PostChangeVisitor();
			try {
				event.getDelta().accept(pcv);
			}
			catch (CoreException ce) {
				AptPlugin.log(ce, "Error during post-change resource event"); //$NON-NLS-1$
			}
		}
	}
	
	private void addGeneratedSrcFolderTo(final Set<IProject> projs, boolean isTestCode){
		
		for(IProject proj : projs ){
			final IJavaProject javaProj = JavaCore.create(proj);
			if(javaProj.getProject().isOpen() && AptConfig.isEnabled(javaProj)){
				final GeneratedSourceFolderManager gsfm = AptPlugin.getAptProject(javaProj).getGeneratedSourceFolderManager(isTestCode);
				gsfm.ensureFolderExists();
			}	
		}

	}
	
	/**
	 * We need a post-change visitor, as there is a bug in the platform for
	 * resource change notification -- some items will be reported *only* in the post-change event,
	 * so we keep track of them here and handle them in the pre-build
	 */
	private class PostChangeVisitor implements IResourceDeltaVisitor {

		@Override
		public boolean visit(IResourceDelta delta) throws CoreException {
			if( delta.getKind() == IResourceDelta.REMOVED ){
				if (AptPlugin.DEBUG_GFM) {
					AptPlugin.trace("generated resource post-change listener adding to deletedResources:" +  //$NON-NLS-1$
							delta.getResource().getName());
				}
				deletedResources.add(delta.getResource());
			}
			
			return true;
		}
		
	}

	private class PreBuildVisitor implements IResourceDeltaVisitor
	{
		// projects that we need to add the generated source folder to.
		private final Set<IProject> _addGenFolderTo = new HashSet<>();
		// projects that we need to add the generated test source folder to.
		private final Set<IProject> _addGenTestFolderTo = new HashSet<>();
		// any projects that is closed or about to be deleted
		private final Set<IProject> _removedProjects = new HashSet<>();
		@Override
		public boolean visit(IResourceDelta delta) throws CoreException 
		{
			IResource r = delta.getResource();
			IProject project = r.getProject();
			
			if ( project == null ) 
				return true;
			
			if( delta.getKind() == IResourceDelta.REMOVED ){
				if (!deletedResources.contains(r)) {
					handleDeletion(r);
				}
			}
			else if( r instanceof IProject ){
				final IProject proj = (IProject)delta.getResource();		
				if( canUpdate(proj) ){
					_addGenFolderTo.add(proj);
					_addGenTestFolderTo.add(proj);
				}				
				else
					_removedProjects.add(proj);
			}

			return true;
		}	
		
		private void handleDeletion(IResource resource) throws CoreException {
			if (AptPlugin.DEBUG_GFM) {
				AptPlugin.trace("handleDeletion: resource = " + resource.getName()); //$NON-NLS-1$
			}
			IProject project = resource.getProject();
			final IJavaProject javaProj = JavaCore.create(project);
			final AptProject aptProj = AptPlugin.getAptProject(javaProj);
			if( resource instanceof IFile ){
				final GeneratedFileManager gfm = aptProj.getGeneratedFileManager(false);
				IFile f = (IFile)resource;
				gfm.fileDeleted(f);
				aptProj.getGeneratedFileManager(true).fileDeleted(f);
			}				
			else if( resource instanceof IFolder ){			
				IFolder f = (IFolder) resource;					
				final GeneratedSourceFolderManager gsfm = aptProj.getGeneratedSourceFolderManager(false);
				if ( gsfm.isGeneratedSourceFolder( f ) ){
					gsfm.folderDeleted();
					// all deletion occurs before any add (adding the generated source directory)
					if( !_removedProjects.contains(project) ){
						_addGenFolderTo.add(project);
					}
					// if the project is already closed or in the process of being
					// deleted, will ignore this deletion since we cannot correct 
					// the classpath anyways.
				}
				final GeneratedSourceFolderManager testgsfm = aptProj.getGeneratedSourceFolderManager(true);
				if ( testgsfm.isGeneratedSourceFolder( f ) ){
					testgsfm.folderDeleted();
					// all deletion occurs before any add (adding the generated source directory)
					if( !_removedProjects.contains(project) ){
						_addGenTestFolderTo.add(project);
					}
					// if the project is already closed or in the process of being
					// deleted, will ignore this deletion since we cannot correct 
					// the classpath anyways.
				}
			}
			else if( resource instanceof IProject ){	
				_removedProjects.add((IProject)resource);
			}
		}
		
		Set<IProject> getProjectsThatNeedGenSrcFolder(){
			_addGenFolderTo.removeAll(_removedProjects);
			return _addGenFolderTo;
		}

		Set<IProject> getProjectsThatNeedGenTestSrcFolder(){
			_addGenTestFolderTo.removeAll(_removedProjects);
			return _addGenTestFolderTo;
		}

		private boolean canUpdate(IProject proj)
			throws CoreException
		{
			return proj.isOpen() && proj.exists() && proj.hasNature(JavaCore.NATURE_ID);
		}
	}
}

Back to the top