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