Line 0
Link Here
|
|
|
1 |
/* |
2 |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
3 |
* |
4 |
* Copyright 2012 Oracle and/or its affiliates. All rights reserved. |
5 |
* |
6 |
* Oracle and Java are registered trademarks of Oracle and/or its affiliates. |
7 |
* Other names may be trademarks of their respective owners. |
8 |
* |
9 |
* The contents of this file are subject to the terms of either the GNU |
10 |
* General Public License Version 2 only ("GPL") or the Common |
11 |
* Development and Distribution License("CDDL") (collectively, the |
12 |
* "License"). You may not use this file except in compliance with the |
13 |
* License. You can obtain a copy of the License at |
14 |
* http://www.netbeans.org/cddl-gplv2.html |
15 |
* or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the |
16 |
* specific language governing permissions and limitations under the |
17 |
* License. When distributing the software, include this License Header |
18 |
* Notice in each file and include the License file at |
19 |
* nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this |
20 |
* particular file as subject to the "Classpath" exception as provided |
21 |
* by Oracle in the GPL Version 2 section of the License file that |
22 |
* accompanied this code. If applicable, add the following below the |
23 |
* License Header, with the fields enclosed by brackets [] replaced by |
24 |
* your own identifying information: |
25 |
* "Portions Copyrighted [year] [name of copyright owner]" |
26 |
* |
27 |
* If you wish your version of this file to be governed by only the CDDL |
28 |
* or only the GPL Version 2, indicate your decision by adding |
29 |
* "[Contributor] elects to include this software in this distribution |
30 |
* under the [CDDL or GPL Version 2] license." If you do not indicate a |
31 |
* single choice of license, a recipient has the option to distribute |
32 |
* your version of this file under either the CDDL, the GPL Version 2 or |
33 |
* to extend the choice of license to its licensees as provided above. |
34 |
* However, if you add GPL Version 2 code and therefore, elected the GPL |
35 |
* Version 2 license, then the option applies only if the new code is |
36 |
* made subject to such option by the copyright holder. |
37 |
* |
38 |
* Contributor(s): |
39 |
* |
40 |
* Portions Copyrighted 2012 Sun Microsystems, Inc. |
41 |
*/ |
42 |
package org.netbeans.modules.java.project; |
43 |
|
44 |
import java.beans.PropertyChangeEvent; |
45 |
import java.beans.PropertyChangeListener; |
46 |
import java.beans.PropertyChangeSupport; |
47 |
import java.io.File; |
48 |
import java.io.IOException; |
49 |
import java.lang.ref.Reference; |
50 |
import java.lang.ref.WeakReference; |
51 |
import java.net.URI; |
52 |
import java.net.URL; |
53 |
import java.util.ArrayDeque; |
54 |
import java.util.ArrayList; |
55 |
import java.util.Arrays; |
56 |
import java.util.Collection; |
57 |
import java.util.Collections; |
58 |
import java.util.HashMap; |
59 |
import java.util.HashSet; |
60 |
import java.util.LinkedHashSet; |
61 |
import java.util.Map; |
62 |
import java.util.Queue; |
63 |
import java.util.Set; |
64 |
import java.util.concurrent.Callable; |
65 |
import java.util.concurrent.ExecutionException; |
66 |
import java.util.concurrent.Future; |
67 |
import java.util.concurrent.FutureTask; |
68 |
import java.util.concurrent.RunnableFuture; |
69 |
import java.util.concurrent.TimeUnit; |
70 |
import java.util.concurrent.TimeoutException; |
71 |
import java.util.concurrent.atomic.AtomicBoolean; |
72 |
import java.util.logging.Level; |
73 |
import java.util.logging.Logger; |
74 |
import java.util.regex.Matcher; |
75 |
import java.util.regex.Pattern; |
76 |
import javax.swing.JFileChooser; |
77 |
import org.netbeans.api.annotations.common.CheckForNull; |
78 |
import org.netbeans.api.annotations.common.NonNull; |
79 |
import org.netbeans.api.annotations.common.NullAllowed; |
80 |
import org.netbeans.api.java.platform.JavaPlatform; |
81 |
import org.netbeans.api.java.platform.JavaPlatformManager; |
82 |
import org.netbeans.api.java.platform.PlatformsCustomizer; |
83 |
import org.netbeans.api.java.platform.Specification; |
84 |
import org.netbeans.api.project.FileOwnerQuery; |
85 |
import org.netbeans.api.project.Project; |
86 |
import org.netbeans.api.project.ProjectManager; |
87 |
import org.netbeans.api.project.ProjectUtils; |
88 |
import org.netbeans.api.project.libraries.LibrariesCustomizer; |
89 |
import org.netbeans.api.project.libraries.Library; |
90 |
import org.netbeans.api.project.libraries.LibraryManager; |
91 |
import org.netbeans.spi.project.libraries.support.LibrariesSupport; |
92 |
import org.netbeans.spi.project.support.ant.AntProjectHelper; |
93 |
import org.netbeans.spi.project.support.ant.EditableProperties; |
94 |
import org.netbeans.spi.project.support.ant.PropertyEvaluator; |
95 |
import org.netbeans.spi.project.support.ant.PropertyUtils; |
96 |
import org.netbeans.spi.project.support.ant.ReferenceHelper; |
97 |
import org.netbeans.spi.project.ui.ProjectProblemsProvider; |
98 |
import org.openide.filesystems.FileObject; |
99 |
import org.openide.util.NbBundle; |
100 |
import org.openide.util.Parameters; |
101 |
|
102 |
import static org.netbeans.modules.java.project.Bundle.*; |
103 |
import org.netbeans.spi.java.project.support.ui.BrokenReferencesSupport; |
104 |
import org.netbeans.spi.project.support.ant.ui.VariablesSupport; |
105 |
import org.netbeans.spi.project.ui.ProjectProblemResolver; |
106 |
import org.netbeans.spi.project.ui.ProjectProblemsProvider.Result; |
107 |
import org.netbeans.spi.project.ui.support.ProjectChooser; |
108 |
import org.openide.DialogDescriptor; |
109 |
import org.openide.DialogDisplayer; |
110 |
import org.openide.modules.SpecificationVersion; |
111 |
import org.openide.util.Exceptions; |
112 |
import org.openide.util.Lookup; |
113 |
import org.openide.util.Mutex; |
114 |
import org.openide.util.RequestProcessor; |
115 |
import org.openide.util.Utilities; |
116 |
import org.openide.util.WeakListeners; |
117 |
|
118 |
/** |
119 |
* |
120 |
* @author Tomas Zezula |
121 |
*/ |
122 |
public class ProjectProblemsProviders { |
123 |
|
124 |
private static final Logger LOG = Logger.getLogger(ProjectProblemsProviders.class.getName()); |
125 |
private static final RequestProcessor RP = new RequestProcessor(ProjectProblemsProviders.class); |
126 |
|
127 |
private ProjectProblemsProviders() { |
128 |
throw new IllegalStateException(String.format("The %s cannot be instantiated.",this.getClass().getName())); //NOI18N |
129 |
} |
130 |
|
131 |
|
132 |
|
133 |
public static ProjectProblemsProvider createReferenceProblemProvider( |
134 |
@NonNull final AntProjectHelper projectHelper, |
135 |
@NonNull final ReferenceHelper referenceHelper, |
136 |
@NonNull final PropertyEvaluator evaluator, |
137 |
@NonNull final String[] properties, |
138 |
@NonNull final String[] platformProperties) { |
139 |
final ReferenceProblemProviderImpl pp = new ReferenceProblemProviderImpl(projectHelper, evaluator, referenceHelper, properties, platformProperties); |
140 |
pp.attachListeners(); |
141 |
return pp; |
142 |
} |
143 |
|
144 |
public static ProjectProblemsProvider createPlatformVersionProblemProvider( |
145 |
@NonNull final AntProjectHelper helper, |
146 |
@NonNull final PropertyEvaluator evaluator, |
147 |
@NullAllowed final Runnable hook, |
148 |
@NonNull final String platformType, |
149 |
@NonNull final String platformProperty, |
150 |
@NonNull final String... platformVersionProperties) { |
151 |
final PlatformVersionProblemProviderImpl pp = new PlatformVersionProblemProviderImpl( |
152 |
helper, |
153 |
evaluator, |
154 |
hook, |
155 |
platformType, |
156 |
platformProperty, |
157 |
platformVersionProperties); |
158 |
pp.attachListeners(); |
159 |
return pp; |
160 |
} |
161 |
|
162 |
//<editor-fold defaultstate="collapsed" desc="Helper Methods & Types"> |
163 |
@NonNull |
164 |
private static Set<? extends ProjectProblemsProvider.ProjectProblem> getReferenceProblems( |
165 |
@NullAllowed final AntProjectHelper helper, |
166 |
@NullAllowed final PropertyEvaluator evaluator, |
167 |
@NullAllowed final ReferenceHelper refHelper, |
168 |
@NonNull final String[] ps, |
169 |
final boolean abortAfterFirstProblem) { |
170 |
Set<ProjectProblemsProvider.ProjectProblem> set = new LinkedHashSet<ProjectProblemsProvider.ProjectProblem>(); |
171 |
StringBuilder all = new StringBuilder(); |
172 |
// this call waits for list of libraries to be refreshhed |
173 |
LibraryManager.getDefault().getLibraries(); |
174 |
if (helper == null || evaluator == null || refHelper == null) { |
175 |
return set; |
176 |
} |
177 |
final Queue<FileResolver> fileReoslvers = new ArrayDeque<FileResolver>(); |
178 |
EditableProperties ep = helper.getProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH); |
179 |
for (String p : ps) { |
180 |
// evaluate given property and tokenize it |
181 |
|
182 |
String prop = evaluator.getProperty(p); |
183 |
if (prop == null) { |
184 |
continue; |
185 |
} |
186 |
LOG.log(Level.FINE, "Evaluated {0}={1}", new Object[] {p, prop}); |
187 |
String[] vals = PropertyUtils.tokenizePath(prop); |
188 |
|
189 |
// no check whether after evaluating there are still some |
190 |
// references which could not be evaluated |
191 |
for (String v : vals) { |
192 |
// we are checking only: project reference, file reference, library reference |
193 |
if (!(v.startsWith("${file.reference.") || v.startsWith("${project.") || v.startsWith("${libs.") || v.startsWith("${var."))) { // NOI18N |
194 |
all.append(v); |
195 |
continue; |
196 |
} |
197 |
if (v.startsWith("${project.")) { // NOI18N |
198 |
// something in the form: "${project.<projID>}/dist/foo.jar" |
199 |
String val = v.substring(2, v.indexOf('}')); // NOI18N |
200 |
set.add( |
201 |
ProjectProblemsProvider.ProjectProblem.createError( |
202 |
getDisplayName(RefType.PROJECT, val), |
203 |
getDescription(RefType.PROJECT, val), |
204 |
new ProjectResolver(val, helper))); |
205 |
} else { |
206 |
final String val = v.substring(2, v.length() - 1); |
207 |
final ProjectProblemsProvider.ProjectProblem problem; |
208 |
if (v.startsWith("${file.reference")) { // NOI18N |
209 |
final FileResolver fr = new FileResolver(val, helper, fileReoslvers); |
210 |
fileReoslvers.offer(fr); |
211 |
problem = ProjectProblemsProvider.ProjectProblem.createError( |
212 |
getDisplayName(RefType.FILE, val), |
213 |
getDescription(RefType.FILE, val), |
214 |
fr); |
215 |
|
216 |
} else if (v.startsWith("${var")) { // NOI18N |
217 |
problem = ProjectProblemsProvider.ProjectProblem.createError( |
218 |
getDisplayName(RefType.VARIABLE, val), |
219 |
getDescription(RefType.VARIABLE, val), |
220 |
new VariableResolver(RefType.VARIABLE, val)); |
221 |
} else { |
222 |
problem = ProjectProblemsProvider.ProjectProblem.createError( |
223 |
getDisplayName(RefType.LIBRARY, val), |
224 |
getDescription(RefType.LIBRARY, val), |
225 |
new LibraryResolver(RefType.LIBRARY, val, refHelper)); |
226 |
} |
227 |
set.add(problem); |
228 |
} |
229 |
if (abortAfterFirstProblem) { |
230 |
break; |
231 |
} |
232 |
} |
233 |
if (set.size() > 0 && abortAfterFirstProblem) { |
234 |
break; |
235 |
} |
236 |
|
237 |
// test that resolved variable based property points to an existing file |
238 |
String path = ep.getProperty(p); |
239 |
if (path != null) { |
240 |
for (String v : PropertyUtils.tokenizePath(path)) { |
241 |
if (v.startsWith("${file.reference.")) { //NOI18N |
242 |
v = ep.getProperty(v.substring(2, v.length() - 1)); |
243 |
} |
244 |
if (v != null && v.startsWith("${var.")) { //NOI18N |
245 |
String value = evaluator.evaluate(v); |
246 |
if (value.startsWith("${var.")) { // NOI18N |
247 |
// this problem was already reported |
248 |
continue; |
249 |
} |
250 |
File f = getFile(helper, evaluator, value); |
251 |
if (f.exists()) { |
252 |
continue; |
253 |
} |
254 |
set.add( |
255 |
ProjectProblemsProvider.ProjectProblem.createError( |
256 |
getDisplayName(RefType.VARIABLE_CONTENT, v), |
257 |
getDescription(RefType.VARIABLE_CONTENT, v), |
258 |
new VariableResolver(RefType.VARIABLE_CONTENT, v))); |
259 |
} |
260 |
} |
261 |
} |
262 |
|
263 |
} |
264 |
|
265 |
// Check also that all referenced project really exist and are reachable. |
266 |
// If they are not report them as broken reference. |
267 |
// XXX: there will be API in PropertyUtils for listing of Ant |
268 |
// prop names in String. Consider using it here. |
269 |
final Map<String, String> entries = evaluator.getProperties(); |
270 |
if (entries == null) { |
271 |
throw new IllegalArgumentException("Properies mapping could not be computed (e.g. due to a circular definition). Evaluator: "+evaluator.toString()); //NOI18N |
272 |
} |
273 |
for (Map.Entry<String, String> entry : entries.entrySet()) { |
274 |
String key = entry.getKey(); |
275 |
String value = entry.getValue(); |
276 |
if (key.startsWith("project.")) { // NOI18N |
277 |
if ("project.license".equals(key)) { //NOI18N |
278 |
continue; |
279 |
} |
280 |
File f = getFile(helper, evaluator, value); |
281 |
if (f.exists()) { |
282 |
continue; |
283 |
} |
284 |
// Check that the value is really used by some property. |
285 |
// If it is not then ignore such a project. |
286 |
if (all.indexOf(value) == -1) { |
287 |
continue; |
288 |
} |
289 |
set.add( |
290 |
ProjectProblemsProvider.ProjectProblem.createError( |
291 |
getDisplayName(RefType.PROJECT, key), |
292 |
getDescription(RefType.PROJECT, key), |
293 |
new ProjectResolver(key, helper))); |
294 |
} |
295 |
else if (key.startsWith("file.reference")) { //NOI18N |
296 |
File f = getFile(helper, evaluator, value); |
297 |
String unevaluatedValue = ep.getProperty(key); |
298 |
boolean alreadyChecked = unevaluatedValue != null ? unevaluatedValue.startsWith("${var.") : false; // NOI18N |
299 |
if (f.exists() || all.indexOf(value) == -1 || alreadyChecked) { // NOI18N |
300 |
continue; |
301 |
} |
302 |
final FileResolver fr = new FileResolver(key, helper, fileReoslvers); |
303 |
fileReoslvers.offer(fr); |
304 |
set.add( |
305 |
ProjectProblemsProvider.ProjectProblem.createError( |
306 |
getDisplayName(RefType.FILE, key), |
307 |
getDescription(RefType.FILE, key), |
308 |
fr)); |
309 |
} |
310 |
} |
311 |
|
312 |
//Check for libbraries with broken classpath content |
313 |
Set<String> usedLibraries = new HashSet<String>(); |
314 |
Pattern libPattern = Pattern.compile("\\$\\{(libs\\.[-._a-zA-Z0-9]+\\.classpath)\\}"); //NOI18N |
315 |
for (String p : ps) { |
316 |
String propertyValue = ep.getProperty(p); |
317 |
if (propertyValue != null) { |
318 |
for (String v : PropertyUtils.tokenizePath(propertyValue)) { |
319 |
Matcher m = libPattern.matcher(v); |
320 |
if (m.matches()) { |
321 |
usedLibraries.add (m.group(1)); |
322 |
} |
323 |
} |
324 |
} |
325 |
} |
326 |
for (String libraryRef : usedLibraries) { |
327 |
String libraryName = libraryRef.substring(5,libraryRef.length()-10); |
328 |
Library lib = refHelper.findLibrary(libraryName); |
329 |
if (lib == null) { |
330 |
// Should already have been caught before? |
331 |
set.add( |
332 |
ProjectProblemsProvider.ProjectProblem.createError( |
333 |
getDisplayName(RefType.LIBRARY, libraryRef), |
334 |
getDescription(RefType.LIBRARY, libraryRef), |
335 |
new LibraryResolver(RefType.LIBRARY, libraryRef, refHelper))); |
336 |
} |
337 |
else { |
338 |
//XXX: Should check all the volumes (sources, javadoc, ...)? |
339 |
for (URI uri : lib.getURIContent("classpath")) { // NOI18N |
340 |
URI uri2 = LibrariesSupport.getArchiveFile(uri); |
341 |
if (uri2 == null) { |
342 |
uri2 = uri; |
343 |
} |
344 |
FileObject fo = LibrariesSupport.resolveLibraryEntryFileObject(lib.getManager().getLocation(), uri2); |
345 |
if (null == fo && !canResolveEvaluatedUri(helper.getStandardPropertyEvaluator(), lib.getManager().getLocation(), uri2)) { |
346 |
set.add( |
347 |
ProjectProblemsProvider.ProjectProblem.createError( |
348 |
getDisplayName(RefType.LIBRARY_CONTENT, libraryRef), |
349 |
getDescription(RefType.LIBRARY_CONTENT, libraryRef), |
350 |
new LibraryResolver(RefType.LIBRARY_CONTENT, libraryRef, refHelper))); |
351 |
break; |
352 |
} |
353 |
} |
354 |
} |
355 |
} |
356 |
|
357 |
return set; |
358 |
} |
359 |
|
360 |
@NonNull |
361 |
private static Set<ProjectProblemsProvider.ProjectProblem> getPlatformProblems( |
362 |
@NullAllowed final PropertyEvaluator evaluator, |
363 |
@NonNull final String[] platformProperties, |
364 |
boolean abortAfterFirstProblem) { |
365 |
final Set<ProjectProblemsProvider.ProjectProblem> set = new LinkedHashSet<ProjectProblemsProvider.ProjectProblem>(); |
366 |
if (evaluator == null) { |
367 |
return set; |
368 |
} |
369 |
for (String pprop : platformProperties) { |
370 |
String prop = evaluator.getProperty(pprop); |
371 |
if (prop == null) { |
372 |
continue; |
373 |
} |
374 |
if (!existPlatform(prop)) { |
375 |
|
376 |
// XXX: the J2ME stores in project.properties also platform |
377 |
// display name and so show this display name instead of just |
378 |
// prop ID if available. |
379 |
if (evaluator.getProperty(pprop + ".description") != null) { // NOI18N |
380 |
prop = evaluator.getProperty(pprop + ".description"); // NOI18N |
381 |
} |
382 |
|
383 |
set.add( |
384 |
ProjectProblemsProvider.ProjectProblem.createError( |
385 |
getDisplayName(RefType.PLATFORM, prop), |
386 |
getDescription(RefType.PLATFORM, prop), |
387 |
new PlatformResolver(prop))); |
388 |
} |
389 |
if (set.size() > 0 && abortAfterFirstProblem) { |
390 |
break; |
391 |
} |
392 |
} |
393 |
return set; |
394 |
} |
395 |
|
396 |
private static File getFile (AntProjectHelper helper, PropertyEvaluator evaluator, String name) { |
397 |
if (helper != null) { |
398 |
return new File(helper.resolvePath(name)); |
399 |
} else { |
400 |
File f = new File(name); |
401 |
if (!f.exists()) { |
402 |
// perhaps the file is relative? |
403 |
String basedir = evaluator.getProperty("basedir"); // NOI18N |
404 |
assert basedir != null; |
405 |
f = new File(new File(basedir), name); |
406 |
} |
407 |
return f; |
408 |
} |
409 |
} |
410 |
|
411 |
/** Tests whether evaluated URI can be resolved. To support library entry |
412 |
* like "${MAVEN_REPO}/struts/struts.jar". |
413 |
*/ |
414 |
private static boolean canResolveEvaluatedUri(PropertyEvaluator eval, URL libBase, URI libUri) { |
415 |
if (libUri.isAbsolute()) { |
416 |
return false; |
417 |
} |
418 |
String path = LibrariesSupport.convertURIToFilePath(libUri); |
419 |
String newPath = eval.evaluate(path); |
420 |
if (newPath.equals(path)) { |
421 |
return false; |
422 |
} |
423 |
URI newUri = LibrariesSupport.convertFilePathToURI(newPath); |
424 |
return null != LibrariesSupport.resolveLibraryEntryFileObject(libBase, newUri); |
425 |
} |
426 |
|
427 |
private static boolean existPlatform(String platform) { |
428 |
if (platform.equals("default_platform")) { // NOI18N |
429 |
return true; |
430 |
} |
431 |
for (JavaPlatform plat : JavaPlatformManager.getDefault().getInstalledPlatforms()) { |
432 |
// XXX: this should be defined as PROPERTY somewhere |
433 |
if (platform.equals(plat.getProperties().get("platform.ant.name")) && // NOI18N |
434 |
plat.getInstallFolders().size() > 0) { |
435 |
return true; |
436 |
} |
437 |
} |
438 |
return false; |
439 |
} |
440 |
|
441 |
@NonNull |
442 |
@NbBundle.Messages({ |
443 |
"LBL_BrokenLinksCustomizer_BrokenLibrary=\"{0}\" library could not be found", |
444 |
"LBL_BrokenLinksCustomizer_BrokenDefinableLibrary=\"{0}\" library must be defined", |
445 |
"LBL_BrokenLinksCustomizer_BrokenLibraryContent=\"{0}\" library has missing items", |
446 |
"LBL_BrokenLinksCustomizer_BrokenProjectReference=\"{0}\" project could not be found", |
447 |
"LBL_BrokenLinksCustomizer_BrokenFileReference=\"{0}\" file/folder could not be found", |
448 |
"LBL_BrokenLinksCustomizer_BrokenVariable=\"{0}\" variable could not be found", |
449 |
"LBL_BrokenLinksCustomizer_BrokenVariableContent=\"{0}\" variable based file/folder could not be found", |
450 |
"LBL_BrokenLinksCustomizer_BrokenPlatform=\"{0}\" platform could not be found", |
451 |
}) |
452 |
private static String getDisplayName( |
453 |
@NonNull final RefType type, |
454 |
@NonNull final String id) { |
455 |
switch (type) { |
456 |
case LIBRARY: |
457 |
return LBL_BrokenLinksCustomizer_BrokenLibrary(getDisplayId(type, id)); |
458 |
case DEFINABLE_LIBRARY: |
459 |
return LBL_BrokenLinksCustomizer_BrokenDefinableLibrary(getDisplayId(type, id)); |
460 |
case LIBRARY_CONTENT: |
461 |
return LBL_BrokenLinksCustomizer_BrokenLibraryContent(getDisplayId(type, id)); |
462 |
case PROJECT: |
463 |
return LBL_BrokenLinksCustomizer_BrokenProjectReference(getDisplayId(type, id)); |
464 |
case FILE: |
465 |
return LBL_BrokenLinksCustomizer_BrokenFileReference(getDisplayId(type, id)); |
466 |
case PLATFORM: |
467 |
return LBL_BrokenLinksCustomizer_BrokenPlatform(getDisplayId(type, id)); |
468 |
case VARIABLE: |
469 |
return LBL_BrokenLinksCustomizer_BrokenVariable(getDisplayId(type, id)); |
470 |
case VARIABLE_CONTENT: |
471 |
return LBL_BrokenLinksCustomizer_BrokenVariableContent(getDisplayId(type, id)); |
472 |
default: |
473 |
assert false; |
474 |
return id; |
475 |
} |
476 |
} |
477 |
|
478 |
@NbBundle.Messages({ |
479 |
"LBL_BrokenLinksCustomizer_BrokenLibraryDesc=Problem: The project uses a class library called \"{0}\", but this class library was not found.\nSolution: Click Resolve to open the Library Manager and create a new class library called \"{0}\".", |
480 |
"LBL_BrokenLinksCustomizer_BrokenDefinableLibraryDesc=Problem: The project uses a class library called \"{0}\", but this class library is not currently defined locally.\nSolution: Click Resolve to download or otherwise automatically define this library.", |
481 |
"LBL_BrokenLinksCustomizer_BrokenLibraryContentDesc=Problem: The project uses the class library called \"{0}\" but the classpath items of this library are missing.\nSolution: Click Resolve to open the Library Manager and locate the missing classpath items of \"{0}\" library.", |
482 |
"LBL_BrokenLinksCustomizer_BrokenProjectReferenceDesc=Problem: The project classpath includes a reference to the project called \"{0}\", but this project was not found.\nSolution: Click Resolve and locate the missing project.", |
483 |
"LBL_BrokenLinksCustomizer_BrokenFileReferenceDesc=Problem: The project uses the file/folder called \"{0}\", but this file/folder was not found.\nSolution: Click Resolve and locate the missing file/folder.", |
484 |
"LBL_BrokenLinksCustomizer_BrokenVariableReferenceDesc=Problem: The project uses the variable called \"{0}\", but this variable was not found.\nSolution: Click Resolve and setup this variable there.", |
485 |
"LBL_BrokenLinksCustomizer_BrokenVariableContentDesc=Problem: The project uses the variable based file/folder \"{0}\", but this file/folder was not found.\nSolution: Click Resolve and update your variable to point to correct location.", |
486 |
"LBL_BrokenLinksCustomizer_BrokenPlatformDesc=Problem: The project uses the Java Platform called \"{0}\", but this platform was not found.\nSolution: Click Resolve and create new platform called \"{0}\"." |
487 |
}) |
488 |
private static String getDescription( |
489 |
@NonNull final RefType type, |
490 |
@NonNull final String id |
491 |
) { |
492 |
switch (type) { |
493 |
case LIBRARY: |
494 |
return LBL_BrokenLinksCustomizer_BrokenLibraryDesc(getDisplayId(type, id)); |
495 |
case DEFINABLE_LIBRARY: |
496 |
return LBL_BrokenLinksCustomizer_BrokenDefinableLibraryDesc(getDisplayId(type, id)); |
497 |
case LIBRARY_CONTENT: |
498 |
return LBL_BrokenLinksCustomizer_BrokenLibraryContentDesc(getDisplayId(type, id)); |
499 |
case PROJECT: |
500 |
return LBL_BrokenLinksCustomizer_BrokenProjectReferenceDesc(getDisplayId(type, id)); |
501 |
case FILE: |
502 |
return LBL_BrokenLinksCustomizer_BrokenFileReferenceDesc(getDisplayId(type, id)); |
503 |
case PLATFORM: |
504 |
return LBL_BrokenLinksCustomizer_BrokenPlatformDesc(getDisplayId(type, id)); |
505 |
case VARIABLE: |
506 |
return LBL_BrokenLinksCustomizer_BrokenVariableReferenceDesc(getDisplayId(type, id)); |
507 |
case VARIABLE_CONTENT: |
508 |
return LBL_BrokenLinksCustomizer_BrokenVariableContent(getDisplayId(type, id)); |
509 |
default: |
510 |
assert false; |
511 |
return id; |
512 |
} |
513 |
} |
514 |
|
515 |
private static String getDisplayId( |
516 |
@NonNull final RefType type, |
517 |
@NonNull final String id) { |
518 |
switch (type) { |
519 |
case LIBRARY: |
520 |
case DEFINABLE_LIBRARY: |
521 |
case LIBRARY_CONTENT: |
522 |
// libs.<name>.classpath |
523 |
return id.substring(5, id.length()-10); |
524 |
case PROJECT: |
525 |
// project.<name> |
526 |
return id.substring(8); |
527 |
case FILE: |
528 |
// file.reference.<name> |
529 |
return id.substring(15); |
530 |
case PLATFORM: |
531 |
return id; |
532 |
case VARIABLE: |
533 |
return id.substring(4, id.indexOf('}')); // NOI18N |
534 |
case VARIABLE_CONTENT: |
535 |
return id.substring(6, id.indexOf('}')) + id.substring(id.indexOf('}')+1); // NOI18N |
536 |
default: |
537 |
assert false; |
538 |
return id; |
539 |
} |
540 |
} |
541 |
|
542 |
private enum RefType { |
543 |
PROJECT, |
544 |
FILE, |
545 |
PLATFORM, |
546 |
LIBRARY, |
547 |
DEFINABLE_LIBRARY, |
548 |
LIBRARY_CONTENT, |
549 |
VARIABLE, |
550 |
VARIABLE_CONTENT, |
551 |
} |
552 |
//</editor-fold> |
553 |
|
554 |
|
555 |
//<editor-fold defaultstate="collapsed" desc="Resolver implementations"> |
556 |
private static abstract class BaseResolver implements ProjectProblemResolver { |
557 |
|
558 |
protected final RefType type; |
559 |
protected final String id; |
560 |
|
561 |
BaseResolver( |
562 |
@NonNull final RefType type, |
563 |
@NonNull final String id) { |
564 |
Parameters.notNull("type", type); //NOI18N |
565 |
Parameters.notNull("id", id); //NOI18N |
566 |
this.type = type; |
567 |
this.id = id; |
568 |
} |
569 |
|
570 |
@Override |
571 |
public final int hashCode() { |
572 |
int result = 17; |
573 |
result = 31 * result + type.hashCode(); |
574 |
result = 31 * result + id.hashCode(); |
575 |
return result; |
576 |
} |
577 |
|
578 |
@Override |
579 |
public final boolean equals(@NullAllowed final Object other) { |
580 |
if (!(other instanceof BaseResolver)) { |
581 |
return false; |
582 |
} |
583 |
final BaseResolver otherResolver = (BaseResolver) other; |
584 |
return type.equals(otherResolver.type) && |
585 |
id.equals(otherResolver.id); |
586 |
} |
587 |
|
588 |
@Override |
589 |
public String toString() { |
590 |
return String.format( |
591 |
"Resolver for %s %s", //NOI18N |
592 |
type, |
593 |
id); |
594 |
} |
595 |
|
596 |
|
597 |
|
598 |
} |
599 |
|
600 |
private static class PlatformResolver extends BaseResolver { |
601 |
|
602 |
PlatformResolver(@NonNull final String id) { |
603 |
super(RefType.PLATFORM, id); |
604 |
} |
605 |
|
606 |
@Override |
607 |
@NonNull |
608 |
public Future<ProjectProblemsProvider.Result> resolve() { |
609 |
PlatformsCustomizer.showCustomizer(null); |
610 |
return new Done(ProjectProblemsProvider.Result.create(ProjectProblemsProvider.Status.RESOLVED)); |
611 |
} |
612 |
|
613 |
} |
614 |
|
615 |
private static class LibraryResolver extends BaseResolver { |
616 |
|
617 |
private final Callable<Library> definer; |
618 |
private final Reference<ReferenceHelper> refHelper; |
619 |
|
620 |
LibraryResolver( |
621 |
@NonNull RefType type, |
622 |
@NonNull final String id, |
623 |
@NonNull final ReferenceHelper refHelper) { |
624 |
this(translate(type, id),id, refHelper); |
625 |
} |
626 |
|
627 |
private LibraryResolver( |
628 |
@NonNull final Object[] typeDefiner/*todo: replace by Pair*/, |
629 |
@NonNull final String id, |
630 |
@NonNull final ReferenceHelper refHelper) { |
631 |
super((RefType)typeDefiner[0],id); |
632 |
this.definer = (Callable<Library>)typeDefiner[1]; |
633 |
this.refHelper = new WeakReference<ReferenceHelper>(refHelper); |
634 |
} |
635 |
|
636 |
@Override |
637 |
@NonNull |
638 |
public Future<ProjectProblemsProvider.Result> resolve() { |
639 |
if (type == RefType.DEFINABLE_LIBRARY) { |
640 |
return resolveByDefiner(); |
641 |
} else { |
642 |
return new Done(ProjectProblemsProvider.Result.create(resolveByLibraryManager())); |
643 |
} |
644 |
} |
645 |
|
646 |
private ProjectProblemsProvider.Status resolveByLibraryManager() { |
647 |
final LibraryManager lm = getProjectLibraryManager(); |
648 |
if (lm == null) { |
649 |
//Closed and freed project |
650 |
return ProjectProblemsProvider.Status.UNRESOLVED; |
651 |
} |
652 |
LibrariesCustomizer.showCustomizer(null,lm); |
653 |
return ProjectProblemsProvider.Status.RESOLVED; |
654 |
} |
655 |
|
656 |
private Future<ProjectProblemsProvider.Result> resolveByDefiner() { |
657 |
assert definer != null; |
658 |
final RunnableFuture<ProjectProblemsProvider.Result> future = |
659 |
new FutureTask<ProjectProblemsProvider.Result>( |
660 |
new Callable<ProjectProblemsProvider.Result>() { |
661 |
@Override |
662 |
public ProjectProblemsProvider.Result call() throws Exception { |
663 |
ProjectProblemsProvider.Status result = ProjectProblemsProvider.Status.UNRESOLVED; |
664 |
try { |
665 |
Library lib = definer.call(); |
666 |
LOG.log(Level.FINE, "found {0}", lib); //NOI18N |
667 |
result = ProjectProblemsProvider.Status.RESOLVED; |
668 |
} catch (Exception x) { |
669 |
LOG.log(Level.INFO, null, x); |
670 |
result = resolveByLibraryManager(); |
671 |
} |
672 |
return ProjectProblemsProvider.Result.create(result); |
673 |
} |
674 |
}); |
675 |
RP.post(future); |
676 |
return future; |
677 |
} |
678 |
|
679 |
@CheckForNull |
680 |
private LibraryManager getProjectLibraryManager() { |
681 |
final ReferenceHelper rh = refHelper.get(); |
682 |
return rh == null ? |
683 |
null: |
684 |
rh.getProjectLibraryManager() != null ? |
685 |
rh.getProjectLibraryManager(): |
686 |
LibraryManager.getDefault(); |
687 |
} |
688 |
|
689 |
@NonNull |
690 |
private static Object[] translate( |
691 |
@NonNull RefType original, |
692 |
@NonNull String id) { |
693 |
Callable<Library> _definer = null; |
694 |
if (original == RefType.LIBRARY) { |
695 |
final String name = id.substring(5, id.length() - 10); |
696 |
for (BrokenReferencesSupport.LibraryDefiner ld : Lookup.getDefault().lookupAll(BrokenReferencesSupport.LibraryDefiner.class)) { |
697 |
_definer = ld.missingLibrary(name); |
698 |
if (_definer != null) { |
699 |
return new Object[] {RefType.DEFINABLE_LIBRARY, _definer}; |
700 |
} |
701 |
} |
702 |
} |
703 |
return new Object[] {original, null}; |
704 |
} |
705 |
} |
706 |
|
707 |
private static class VariableResolver extends BaseResolver { |
708 |
VariableResolver(@NonNull final RefType type, @NonNull final String id) { |
709 |
super(type, id); |
710 |
} |
711 |
|
712 |
@Override |
713 |
@NonNull |
714 |
public Future<ProjectProblemsProvider.Result> resolve() { |
715 |
VariablesSupport.showVariablesCustomizer(); |
716 |
return new Done(ProjectProblemsProvider.Result.create(ProjectProblemsProvider.Status.RESOLVED)); |
717 |
} |
718 |
} |
719 |
|
720 |
private static abstract class ReferenceResolver extends BaseResolver { |
721 |
|
722 |
static File lastSelectedFile; |
723 |
|
724 |
private final Reference<AntProjectHelper> antProjectHelper; |
725 |
|
726 |
ReferenceResolver( |
727 |
@NonNull final RefType type, |
728 |
@NonNull final String id, |
729 |
@NonNull final AntProjectHelper antProjectHelper) { |
730 |
super (type, id); |
731 |
this.antProjectHelper = new WeakReference<AntProjectHelper>(antProjectHelper); |
732 |
} |
733 |
|
734 |
abstract void updateReference(@NonNull final File file); |
735 |
|
736 |
final void updateReferenceImpl(@NonNull final File file) { |
737 |
final String reference = id; |
738 |
final AntProjectHelper helper = antProjectHelper.get(); |
739 |
if (helper == null) { |
740 |
//Closed and freed project, ignore |
741 |
return; |
742 |
} |
743 |
FileObject myProjDirFO = helper.getProjectDirectory(); |
744 |
final String propertiesFile = AntProjectHelper.PRIVATE_PROPERTIES_PATH; |
745 |
final String path = file.getAbsolutePath(); |
746 |
Project p; |
747 |
try { |
748 |
p = ProjectManager.getDefault().findProject(myProjDirFO); |
749 |
} catch (IOException ex) { |
750 |
Exceptions.printStackTrace(ex); |
751 |
p = null; |
752 |
} |
753 |
final Project proj = p; |
754 |
ProjectManager.mutex().postWriteRequest(new Runnable() { |
755 |
public @Override void run() { |
756 |
EditableProperties props = helper.getProperties(propertiesFile); |
757 |
if (!path.equals(props.getProperty(reference))) { |
758 |
props.setProperty(reference, path); |
759 |
helper.putProperties(propertiesFile, props); |
760 |
} |
761 |
|
762 |
if (proj != null) { |
763 |
try { |
764 |
ProjectManager.getDefault().saveProject(proj); |
765 |
} catch (IOException ex) { |
766 |
Exceptions.printStackTrace(ex); |
767 |
} |
768 |
} |
769 |
} |
770 |
}); |
771 |
} |
772 |
} |
773 |
|
774 |
private static class ProjectResolver extends ReferenceResolver { |
775 |
ProjectResolver(@NonNull final String id, @NonNull AntProjectHelper antProjectHelper) { |
776 |
super (RefType.PROJECT, id, antProjectHelper); |
777 |
} |
778 |
|
779 |
@Override |
780 |
@NonNull |
781 |
@NbBundle.Messages({ |
782 |
"LBL_BrokenLinksCustomizer_Resolve_Project=Browse Project \"{0}\"", |
783 |
}) |
784 |
public Future<ProjectProblemsProvider.Result> resolve() { |
785 |
ProjectProblemsProvider.Status result = ProjectProblemsProvider.Status.UNRESOLVED; |
786 |
final JFileChooser chooser = ProjectChooser.projectChooser(); |
787 |
chooser.setDialogTitle(LBL_BrokenLinksCustomizer_Resolve_Project(getDisplayId(type, id))); |
788 |
if (lastSelectedFile != null) { |
789 |
chooser.setSelectedFile(lastSelectedFile); |
790 |
} |
791 |
int option = chooser.showOpenDialog(null); |
792 |
if (option == JFileChooser.APPROVE_OPTION) { |
793 |
updateReference(chooser.getSelectedFile()); |
794 |
lastSelectedFile = chooser.getSelectedFile(); |
795 |
result = ProjectProblemsProvider.Status.RESOLVED; |
796 |
} |
797 |
return new Done(ProjectProblemsProvider.Result.create(result)); |
798 |
} |
799 |
|
800 |
@Override |
801 |
void updateReference(@NonNull final File file) { |
802 |
updateReferenceImpl(file); |
803 |
} |
804 |
|
805 |
} |
806 |
|
807 |
private static class FileResolver extends ReferenceResolver { |
808 |
|
809 |
private final Queue<? extends FileResolver> peers; |
810 |
private ProjectProblemsProvider.Status resolved = |
811 |
ProjectProblemsProvider.Status.UNRESOLVED; |
812 |
|
813 |
FileResolver( |
814 |
@NonNull final String id, |
815 |
@NonNull final AntProjectHelper antProjectHelper, |
816 |
@NonNull final Queue<? extends FileResolver> peers) { |
817 |
super(RefType.FILE, id, antProjectHelper); |
818 |
this.peers = peers; |
819 |
} |
820 |
|
821 |
@Override |
822 |
@NonNull |
823 |
@NbBundle.Messages({ |
824 |
"LBL_BrokenLinksCustomizer_Resolve_File=Browse \"{0}\"" |
825 |
}) |
826 |
public Future<ProjectProblemsProvider.Result> resolve() { |
827 |
final JFileChooser chooser = new JFileChooser(); |
828 |
chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES); |
829 |
chooser.setDialogTitle(LBL_BrokenLinksCustomizer_Resolve_File(getDisplayId(type, id))); |
830 |
if (lastSelectedFile != null) { |
831 |
chooser.setSelectedFile(lastSelectedFile); |
832 |
} |
833 |
int option = chooser.showOpenDialog(null); |
834 |
if (option == JFileChooser.APPROVE_OPTION) { |
835 |
updateReference(chooser.getSelectedFile()); |
836 |
lastSelectedFile = chooser.getSelectedFile(); |
837 |
resolved = ProjectProblemsProvider.Status.RESOLVED; |
838 |
} |
839 |
return new Done(ProjectProblemsProvider.Result.create(resolved)); |
840 |
} |
841 |
|
842 |
@Override |
843 |
void updateReference(@NonNull final File file) { |
844 |
updateReferenceImpl(file); |
845 |
final File parentFolder = file.getParentFile(); |
846 |
for (FileResolver peer : peers) { |
847 |
if (this != peer && peer.resolved == ProjectProblemsProvider.Status.UNRESOLVED) { |
848 |
final File f = new File(parentFolder, getDisplayId(type, id)); |
849 |
if (f.exists()) { |
850 |
updateReferenceImpl(f); |
851 |
} |
852 |
} |
853 |
} |
854 |
} |
855 |
|
856 |
|
857 |
} |
858 |
|
859 |
private static class SourceTargetResolver implements ProjectProblemResolver { |
860 |
|
861 |
private final String type; |
862 |
private final String platformProp; |
863 |
private final Collection<? extends String> invalidVersionProps; |
864 |
private final SpecificationVersion minVersion; |
865 |
private final SpecificationVersion platformVersion; |
866 |
private final Reference<AntProjectHelper> helperRef; |
867 |
private final Runnable hook; |
868 |
|
869 |
SourceTargetResolver( |
870 |
@NonNull final AntProjectHelper helper, |
871 |
@NullAllowed final Runnable hook, |
872 |
@NonNull final String type, |
873 |
@NonNull final String platformProp, |
874 |
@NonNull final Collection<? extends String> invalidVersionProps, |
875 |
@NonNull final SpecificationVersion minVersion, |
876 |
@NonNull final SpecificationVersion platformVersion) { |
877 |
Parameters.notNull("helper", helper); //NOI18N |
878 |
Parameters.notNull("type", type); //NOI18N |
879 |
Parameters.notNull("platformProp", platformProp); //NOI18N |
880 |
Parameters.notNull("invalidVersionProps", invalidVersionProps); //NOI18N |
881 |
Parameters.notNull("minVersion", minVersion); //NOI18N |
882 |
Parameters.notNull("platformVersion", platformVersion); //NOI18N |
883 |
this.helperRef = new WeakReference<AntProjectHelper>(helper); |
884 |
this.hook = hook; |
885 |
this.type = type; |
886 |
this.platformProp = platformProp; |
887 |
this.invalidVersionProps = invalidVersionProps; |
888 |
this.minVersion = minVersion; |
889 |
this.platformVersion = platformVersion; |
890 |
} |
891 |
|
892 |
|
893 |
@NbBundle.Messages({"LBL_ResolveJDKVersion=Resolve Invalid Java Platform Version - \"{0}\" Project"}) |
894 |
@Override |
895 |
public Future<Result> resolve() { |
896 |
final AntProjectHelper helper = helperRef.get(); |
897 |
if (helper != null) { |
898 |
final Project project = FileOwnerQuery.getOwner(helper.getProjectDirectory()); |
899 |
final FixProjectSourceLevel changeVersion = new FixProjectSourceLevel(type, minVersion, platformVersion); |
900 |
final DialogDescriptor dd = new DialogDescriptor(changeVersion, LBL_ResolveJDKVersion(ProjectUtils.getInformation(project).getDisplayName())); |
901 |
if (DialogDisplayer.getDefault().notify(dd) == DialogDescriptor.OK_OPTION) { |
902 |
final Callable<ProjectProblemsProvider.Result> resultFnc = |
903 |
new Callable<Result>() { |
904 |
@Override |
905 |
public Result call() throws Exception { |
906 |
if (changeVersion.isDowngradeLevel()) { |
907 |
final EditableProperties props = helper.getProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH); |
908 |
for (String p : invalidVersionProps) { |
909 |
props.put(p, platformVersion.toString()); |
910 |
} |
911 |
helper.putProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH, props); |
912 |
ProjectManager.getDefault().saveProject(FileOwnerQuery.getOwner(helper.getProjectDirectory())); |
913 |
return ProjectProblemsProvider.Result.create(ProjectProblemsProvider.Status.RESOLVED); |
914 |
} else { |
915 |
final JavaPlatform jp = changeVersion.getSelectedPlatform(); |
916 |
if (jp != null) { |
917 |
final String antName = jp.getProperties().get("platform.ant.name"); //NOI18N |
918 |
if (antName != null) { |
919 |
final EditableProperties props = helper.getProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH); |
920 |
props.setProperty(platformProp, antName); |
921 |
helper.putProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH, props); |
922 |
if (hook != null) { |
923 |
hook.run(); |
924 |
} |
925 |
ProjectManager.getDefault().saveProject(project); |
926 |
return ProjectProblemsProvider.Result.create(ProjectProblemsProvider.Status.RESOLVED); |
927 |
} |
928 |
} |
929 |
} |
930 |
return ProjectProblemsProvider.Result.create(ProjectProblemsProvider.Status.UNRESOLVED); |
931 |
} |
932 |
}; |
933 |
final RunnableFuture<Result> result = new FutureTask<Result>(resultFnc); |
934 |
RP.post(result); |
935 |
return result; |
936 |
} |
937 |
} |
938 |
return new Done( |
939 |
Result.create(ProjectProblemsProvider.Status.UNRESOLVED)); |
940 |
} |
941 |
|
942 |
@Override |
943 |
public boolean equals(Object other) { |
944 |
if (!(other instanceof SourceTargetResolver)) { |
945 |
return false; |
946 |
} |
947 |
return true; |
948 |
} |
949 |
|
950 |
@Override |
951 |
public int hashCode() { |
952 |
return 17; |
953 |
|
954 |
} |
955 |
|
956 |
|
957 |
} |
958 |
|
959 |
private static final class Done implements Future<ProjectProblemsProvider.Result> { |
960 |
|
961 |
private final ProjectProblemsProvider.Result result; |
962 |
|
963 |
Done(@NonNull final ProjectProblemsProvider.Result result) { |
964 |
Parameters.notNull("result", result); //NOI18N |
965 |
this.result = result; |
966 |
} |
967 |
|
968 |
@Override |
969 |
public boolean cancel(boolean mayInterruptIfRunning) { |
970 |
return false; |
971 |
} |
972 |
|
973 |
@Override |
974 |
public boolean isCancelled() { |
975 |
return false; |
976 |
} |
977 |
|
978 |
@Override |
979 |
public boolean isDone() { |
980 |
return true; |
981 |
} |
982 |
|
983 |
@Override |
984 |
public ProjectProblemsProvider.Result get() throws InterruptedException, ExecutionException { |
985 |
return result; |
986 |
} |
987 |
|
988 |
@Override |
989 |
public ProjectProblemsProvider.Result get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { |
990 |
return get(); |
991 |
} |
992 |
|
993 |
} |
994 |
//</editor-fold> |
995 |
|
996 |
//<editor-fold defaultstate="collapsed" desc="ProjectProblemsProvider implementations"> |
997 |
private static final class ReferenceProblemProviderImpl implements ProjectProblemsProvider, PropertyChangeListener { |
998 |
|
999 |
private final PropertyChangeSupport support = new PropertyChangeSupport(this); |
1000 |
|
1001 |
private final Object problemsLock = new Object(); |
1002 |
//@GuardedBy("problemsLock") |
1003 |
private Collection<? extends ProjectProblem> problems; |
1004 |
//@GuardedBy("problemsLock") |
1005 |
private long eventId; |
1006 |
private final AtomicBoolean listenersInitialized = new AtomicBoolean(); |
1007 |
|
1008 |
private final AntProjectHelper helper; |
1009 |
private final PropertyEvaluator eval; |
1010 |
private final ReferenceHelper refHelper; |
1011 |
private final String[] refProps; |
1012 |
private final String[] platformProps; |
1013 |
|
1014 |
private Map<URL,Object[]> activeLibManLocs; |
1015 |
|
1016 |
|
1017 |
ReferenceProblemProviderImpl( |
1018 |
@NonNull final AntProjectHelper helper, |
1019 |
@NonNull final PropertyEvaluator eval, |
1020 |
@NonNull final ReferenceHelper refHelper, |
1021 |
@NonNull final String[] refProps, |
1022 |
@NonNull final String[] platformProps) { |
1023 |
assert helper != null; |
1024 |
assert eval != null; |
1025 |
assert refHelper != null; |
1026 |
assert refProps != null; |
1027 |
assert platformProps != null; |
1028 |
this.helper = helper; |
1029 |
this.eval = eval; |
1030 |
this.refHelper = refHelper; |
1031 |
this.refProps = Arrays.copyOf(refProps, refProps.length); |
1032 |
this.platformProps = Arrays.copyOf(platformProps, platformProps.length); |
1033 |
} |
1034 |
|
1035 |
@Override |
1036 |
public void addPropertyChangeListener(@NonNull final PropertyChangeListener listener) { |
1037 |
Parameters.notNull("listener", listener); //NOI18N |
1038 |
support.addPropertyChangeListener(listener); |
1039 |
} |
1040 |
|
1041 |
@Override |
1042 |
public void removePropertyChangeListener(@NonNull final PropertyChangeListener listener) { |
1043 |
Parameters.notNull("listener", listener); //NOI18N |
1044 |
support.removePropertyChangeListener(listener); |
1045 |
} |
1046 |
|
1047 |
@Override |
1048 |
public Collection<? extends ProjectProblem> getProblems() { |
1049 |
Collection<? extends ProjectProblem> curProblems; |
1050 |
long curEventId; |
1051 |
synchronized (problemsLock) { |
1052 |
curProblems = problems; |
1053 |
curEventId = eventId; |
1054 |
} |
1055 |
if (curProblems != null) { |
1056 |
return curProblems; |
1057 |
} |
1058 |
curProblems = ProjectManager.mutex().readAccess( |
1059 |
new Mutex.Action<Collection<? extends ProjectProblem>>(){ |
1060 |
@Override |
1061 |
public Collection<? extends ProjectProblem> run() { |
1062 |
final Set<ProjectProblem> newProblems = new LinkedHashSet<ProjectProblem>(); |
1063 |
newProblems.addAll(getReferenceProblems(helper,eval,refHelper,refProps,false)); |
1064 |
newProblems.addAll(getPlatformProblems(eval,platformProps,false)); |
1065 |
return Collections.unmodifiableSet(newProblems); |
1066 |
} |
1067 |
}); |
1068 |
synchronized (problemsLock){ |
1069 |
if (curEventId == eventId) { |
1070 |
//No canonical mapping needed |
1071 |
problems = curProblems; |
1072 |
} else if (problems != null) { |
1073 |
curProblems = problems; |
1074 |
} |
1075 |
} |
1076 |
assert curProblems != null; |
1077 |
return curProblems; |
1078 |
} |
1079 |
|
1080 |
@Override |
1081 |
public void propertyChange(PropertyChangeEvent evt) { |
1082 |
if (LibraryManager.PROP_OPEN_LIBRARY_MANAGERS.equals(evt.getPropertyName())) { |
1083 |
addLibraryManagerListener(); |
1084 |
} |
1085 |
synchronized (problemsLock) { |
1086 |
problems = null; |
1087 |
eventId++; |
1088 |
} |
1089 |
support.firePropertyChange(PROP_PROBLEMS,null,null); |
1090 |
} |
1091 |
|
1092 |
void attachListeners() { |
1093 |
if (listenersInitialized.compareAndSet(false, true)) { |
1094 |
eval.addPropertyChangeListener(this); |
1095 |
JavaPlatformManager.getDefault().addPropertyChangeListener(WeakListeners.propertyChange(this, JavaPlatformManager.getDefault())); |
1096 |
LibraryManager.addOpenManagersPropertyChangeListener(new OpenManagersWeakListener(this)); |
1097 |
addLibraryManagerListener(); |
1098 |
} else { |
1099 |
throw new IllegalStateException(); |
1100 |
} |
1101 |
} |
1102 |
|
1103 |
private void addLibraryManagerListener() { |
1104 |
final Map<URL,Object[]> oldLMs; |
1105 |
final boolean attachToDefault; |
1106 |
synchronized (this) { |
1107 |
attachToDefault = activeLibManLocs == null; |
1108 |
if (attachToDefault) { |
1109 |
activeLibManLocs = new HashMap<URL,Object[]>(); |
1110 |
} |
1111 |
oldLMs = new HashMap<URL,Object[]>(activeLibManLocs); |
1112 |
} |
1113 |
if (attachToDefault) { |
1114 |
final LibraryManager manager = LibraryManager.getDefault(); |
1115 |
manager.addPropertyChangeListener(WeakListeners.propertyChange(this, manager)); |
1116 |
} |
1117 |
final Collection<? extends LibraryManager> managers = LibraryManager.getOpenManagers(); |
1118 |
final Map<URL,LibraryManager> managerByLocation = new HashMap<URL, LibraryManager>(); |
1119 |
for (LibraryManager manager : managers) { |
1120 |
final URL url = manager.getLocation(); |
1121 |
if (url != null) { |
1122 |
managerByLocation.put(url, manager); |
1123 |
} |
1124 |
} |
1125 |
final HashMap<URL,Object[]> toRemove = new HashMap<URL,Object[]>(oldLMs); |
1126 |
toRemove.keySet().removeAll(managerByLocation.keySet()); |
1127 |
for (Object[] pair : toRemove.values()) { |
1128 |
((LibraryManager)pair[0]).removePropertyChangeListener((PropertyChangeListener)pair[1]); |
1129 |
} |
1130 |
managerByLocation.keySet().removeAll(oldLMs.keySet()); |
1131 |
final HashMap<URL,Object[]> toAdd = new HashMap<URL,Object[]>(); |
1132 |
for (Map.Entry<URL,LibraryManager> e : managerByLocation.entrySet()) { |
1133 |
final LibraryManager manager = e.getValue(); |
1134 |
final PropertyChangeListener listener = WeakListeners.propertyChange(this, manager); |
1135 |
manager.addPropertyChangeListener(listener); |
1136 |
toAdd.put(e.getKey(), new Object[] {manager, listener}); |
1137 |
} |
1138 |
synchronized (this) { |
1139 |
activeLibManLocs.keySet().removeAll(toRemove.keySet()); |
1140 |
activeLibManLocs.putAll(toAdd); |
1141 |
} |
1142 |
} |
1143 |
|
1144 |
} |
1145 |
|
1146 |
private static final class PlatformVersionProblemProviderImpl implements ProjectProblemsProvider, PropertyChangeListener { |
1147 |
|
1148 |
private final PropertyChangeSupport support = new PropertyChangeSupport(this); |
1149 |
private final Object problemsLock = new Object(); |
1150 |
//@GuardedBy("problemsLock") |
1151 |
private Collection<? extends ProjectProblem> problems; |
1152 |
//@GuardedBy("problemsLock") |
1153 |
private long eventId; |
1154 |
private final AtomicBoolean listenersInitialized = new AtomicBoolean(); |
1155 |
|
1156 |
private final AntProjectHelper helper; |
1157 |
private final PropertyEvaluator eval; |
1158 |
private final Runnable hook; |
1159 |
private final String platformType; |
1160 |
private final String platformProp; |
1161 |
private final Set<String> versionProps; |
1162 |
|
1163 |
PlatformVersionProblemProviderImpl( |
1164 |
@NonNull final AntProjectHelper helper, |
1165 |
@NonNull final PropertyEvaluator eval, |
1166 |
@NullAllowed final Runnable hook, |
1167 |
@NonNull final String platformType, |
1168 |
@NonNull final String platformProp, |
1169 |
@NonNull final String... versionProps) { |
1170 |
assert helper != null; |
1171 |
assert eval != null; |
1172 |
assert platformType != null; |
1173 |
assert platformProp != null; |
1174 |
assert versionProps != null; |
1175 |
this.helper = helper; |
1176 |
this.eval = eval; |
1177 |
this.hook = hook; |
1178 |
this.platformType = platformType; |
1179 |
this.platformProp = platformProp; |
1180 |
this.versionProps = new HashSet<String>(Arrays.asList(versionProps)); |
1181 |
} |
1182 |
|
1183 |
@Override |
1184 |
public void addPropertyChangeListener(@NonNull final PropertyChangeListener listener) { |
1185 |
Parameters.notNull("listener", listener); //NOI18N |
1186 |
support.addPropertyChangeListener(listener); |
1187 |
} |
1188 |
|
1189 |
@Override |
1190 |
public void removePropertyChangeListener(@NonNull final PropertyChangeListener listener) { |
1191 |
Parameters.notNull("listener", listener); //NOI18N |
1192 |
support.removePropertyChangeListener(listener); |
1193 |
} |
1194 |
|
1195 |
@Override |
1196 |
@NbBundle.Messages({ |
1197 |
"LBL_Invalid_JDK_Version=Invalid Java Platform Version", |
1198 |
"HINT_Invalid_JDK_Vernsion=The active project platform is an older version than it's required by project source/binary format." |
1199 |
}) |
1200 |
public Collection<? extends ProjectProblem> getProblems() { |
1201 |
Collection<? extends ProjectProblem> curProblems; |
1202 |
long curEventId; |
1203 |
synchronized (problemsLock) { |
1204 |
curProblems = problems; |
1205 |
curEventId = eventId; |
1206 |
} |
1207 |
if (curProblems != null) { |
1208 |
return curProblems; |
1209 |
} |
1210 |
curProblems = ProjectManager.mutex().readAccess( |
1211 |
new Mutex.Action<Collection<? extends ProjectProblem>>() { |
1212 |
@Override |
1213 |
public Collection<? extends ProjectProblem> run() { |
1214 |
final JavaPlatform activePlatform = getActivePlatform(); |
1215 |
final SpecificationVersion platformVersion = activePlatform == null ? |
1216 |
null: |
1217 |
activePlatform.getSpecification().getVersion(); |
1218 |
final Collection<String> invalidVersionProps = new ArrayList<String>(versionProps.size()); |
1219 |
SpecificationVersion minVersion = getInvalidJdkVersion( |
1220 |
platformVersion, |
1221 |
invalidVersionProps); |
1222 |
return minVersion != null ? |
1223 |
Collections.singleton(ProjectProblem.createError( |
1224 |
LBL_Invalid_JDK_Version(), |
1225 |
HINT_Invalid_JDK_Vernsion(), |
1226 |
new SourceTargetResolver( |
1227 |
helper, |
1228 |
hook, |
1229 |
platformType, |
1230 |
platformProp, |
1231 |
invalidVersionProps, |
1232 |
minVersion, |
1233 |
platformVersion))) : |
1234 |
Collections.<ProjectProblem>emptySet(); |
1235 |
} |
1236 |
}); |
1237 |
synchronized (problemsLock) { |
1238 |
if (curEventId == eventId) { |
1239 |
//No canonical mapping needed |
1240 |
problems = curProblems; |
1241 |
} else if (problems != null) { |
1242 |
curProblems = problems; |
1243 |
} |
1244 |
} |
1245 |
assert curProblems != null; |
1246 |
return curProblems; |
1247 |
} |
1248 |
|
1249 |
@Override |
1250 |
public void propertyChange(PropertyChangeEvent evt) { |
1251 |
final String propName = evt.getPropertyName(); |
1252 |
if (propName == null || platformProp.equals(propName) || versionProps.contains(propName)) { |
1253 |
synchronized (problemsLock) { |
1254 |
problems = null; |
1255 |
eventId++; |
1256 |
} |
1257 |
support.firePropertyChange(PROP_PROBLEMS,null,null); |
1258 |
} |
1259 |
} |
1260 |
|
1261 |
void attachListeners() { |
1262 |
if (listenersInitialized.compareAndSet(false, true)) { |
1263 |
eval.addPropertyChangeListener(this); |
1264 |
} else { |
1265 |
throw new IllegalStateException(); |
1266 |
} |
1267 |
} |
1268 |
|
1269 |
/** |
1270 |
* Gets minimal required JDK version or null if the project platform |
1271 |
* satisfy the required JDK versions. |
1272 |
* @return The minimal {@link SpecificationVersion} of platform or null. |
1273 |
*/ |
1274 |
@CheckForNull |
1275 |
private SpecificationVersion getInvalidJdkVersion ( |
1276 |
@NullAllowed final SpecificationVersion platformVersion, |
1277 |
@NonNull final Collection<? super String> invalidVersionProps) { |
1278 |
SpecificationVersion minVersion = null; |
1279 |
if (platformVersion != null) { |
1280 |
for (String vp : versionProps) { |
1281 |
final String value = this.eval.getProperty(vp); |
1282 |
if (value == null || value.isEmpty()) { |
1283 |
continue; |
1284 |
} |
1285 |
try { |
1286 |
final SpecificationVersion vpVersion = new SpecificationVersion (value); |
1287 |
if (vpVersion.compareTo(platformVersion) > 0) { |
1288 |
invalidVersionProps.add(vp); |
1289 |
minVersion = max(minVersion,vpVersion); |
1290 |
} |
1291 |
} catch (NumberFormatException nfe) { |
1292 |
LOG.log( |
1293 |
Level.WARNING, |
1294 |
"Property: {0} holds non valid version: {1}", //NOI18N |
1295 |
new Object[]{ |
1296 |
vp, |
1297 |
value |
1298 |
}); |
1299 |
} |
1300 |
} |
1301 |
} |
1302 |
return minVersion; |
1303 |
} |
1304 |
|
1305 |
@CheckForNull |
1306 |
private SpecificationVersion max ( |
1307 |
@NullAllowed final SpecificationVersion a, |
1308 |
@NullAllowed final SpecificationVersion b) { |
1309 |
if (a == null) { |
1310 |
return b; |
1311 |
} else if (b == null) { |
1312 |
return a; |
1313 |
} else if (a.compareTo(b)>=0) { |
1314 |
return a; |
1315 |
} else { |
1316 |
return b; |
1317 |
} |
1318 |
} |
1319 |
|
1320 |
@CheckForNull |
1321 |
private JavaPlatform getActivePlatform() { |
1322 |
final String activePlatformId = this.eval.getProperty(platformProp); |
1323 |
final JavaPlatformManager pm = JavaPlatformManager.getDefault(); |
1324 |
if (activePlatformId == null) { |
1325 |
return pm.getDefaultPlatform(); |
1326 |
} |
1327 |
final JavaPlatform[] installedPlatforms = pm.getPlatforms( |
1328 |
null, |
1329 |
new Specification(platformType, null)); |
1330 |
for (JavaPlatform javaPlatform : installedPlatforms) { |
1331 |
final String antName = javaPlatform.getProperties().get("platform.ant.name"); //NOI18N |
1332 |
if (activePlatformId.equals(antName)) { |
1333 |
return javaPlatform; |
1334 |
} |
1335 |
} |
1336 |
return null; |
1337 |
} |
1338 |
} |
1339 |
|
1340 |
private static class OpenManagersWeakListener extends WeakReference<PropertyChangeListener> implements Runnable, PropertyChangeListener { |
1341 |
|
1342 |
public OpenManagersWeakListener(final PropertyChangeListener listener) { |
1343 |
super(listener, Utilities.activeReferenceQueue()); |
1344 |
} |
1345 |
|
1346 |
@Override |
1347 |
public void run() { |
1348 |
LibraryManager.removeOpenManagersPropertyChangeListener(this); |
1349 |
} |
1350 |
|
1351 |
@Override |
1352 |
public void propertyChange(PropertyChangeEvent evt) { |
1353 |
final PropertyChangeListener listener = get(); |
1354 |
if (listener != null) { |
1355 |
listener.propertyChange(evt); |
1356 |
} |
1357 |
} |
1358 |
|
1359 |
} |
1360 |
//</editor-fold> |
1361 |
} |