Lines 49-63
Link Here
|
49 |
import java.lang.ref.WeakReference; |
49 |
import java.lang.ref.WeakReference; |
50 |
import java.net.MalformedURLException; |
50 |
import java.net.MalformedURLException; |
51 |
import java.net.URI; |
51 |
import java.net.URI; |
52 |
import java.net.URISyntaxException; |
|
|
53 |
import java.net.URL; |
52 |
import java.net.URL; |
54 |
import java.util.Collection; |
|
|
55 |
import java.util.Collections; |
56 |
import java.util.HashMap; |
57 |
import java.util.LinkedList; |
58 |
import java.util.Map; |
59 |
import java.util.Set; |
60 |
import java.util.WeakHashMap; |
61 |
import java.util.logging.Level; |
53 |
import java.util.logging.Level; |
62 |
import java.util.logging.Logger; |
54 |
import java.util.logging.Logger; |
63 |
import java.util.prefs.BackingStoreException; |
55 |
import java.util.prefs.BackingStoreException; |
Lines 69-271
Link Here
|
69 |
import org.openide.filesystems.FileStateInvalidException; |
61 |
import org.openide.filesystems.FileStateInvalidException; |
70 |
import org.openide.filesystems.URLMapper; |
62 |
import org.openide.filesystems.URLMapper; |
71 |
import org.openide.util.NbPreferences; |
63 |
import org.openide.util.NbPreferences; |
72 |
import org.openide.util.Utilities; |
64 |
import org.openide.util.lookup.ServiceProvider; |
73 |
import org.openide.util.WeakSet; |
|
|
74 |
|
65 |
|
75 |
/** |
66 |
/** |
76 |
* Finds a project by searching the directory tree. |
67 |
* Finds a project by searching the directory tree. |
77 |
* @author Jesse Glick |
68 |
* @author Jesse Glick |
78 |
*/ |
69 |
*/ |
79 |
@org.openide.util.lookup.ServiceProvider(service=org.netbeans.spi.project.FileOwnerQueryImplementation.class, position=100) |
70 |
@ServiceProvider(service=FileOwnerQueryImplementation.class, position=100) |
80 |
public class SimpleFileOwnerQueryImplementation implements FileOwnerQueryImplementation { |
71 |
public class SimpleFileOwnerQueryImplementation implements FileOwnerQueryImplementation { |
81 |
private static final Logger LOG = Logger.getLogger(SimpleFileOwnerQueryImplementation.class.getName()); |
72 |
private static final Logger LOG = Logger.getLogger(SimpleFileOwnerQueryImplementation.class.getName()); |
82 |
|
73 |
|
83 |
/** Do nothing */ |
74 |
private static URI lastFoundKey = null; |
84 |
public SimpleFileOwnerQueryImplementation() {} |
75 |
private static Reference<Project> lastFoundValue = null; |
85 |
|
76 |
|
86 |
public Project getOwner(URI fileURI) { |
77 |
public static synchronized void resetLastFoundReferences() { // #111892 |
87 |
// Try to find a FileObject for it. |
78 |
lastFoundValue = null; |
88 |
URI test = fileURI; |
79 |
lastFoundKey = null; |
89 |
FileObject file; |
|
|
90 |
do { |
91 |
file = uri2FileObject(test); |
92 |
test = goUp(test); |
93 |
} while (file == null && test != null); |
94 |
if (file == null) { |
95 |
return null; |
96 |
} |
97 |
return getOwner(file); |
98 |
} |
80 |
} |
99 |
|
81 |
|
100 |
private final Set<FileObject> warnedAboutBrokenProjects = new WeakSet<FileObject>(); |
82 |
public @Override Project getOwner(FileObject f) { |
101 |
|
83 |
return getOwner(fileObject2URI(f), f); |
102 |
private Reference<FileObject> lastFoundKey = null; |
84 |
} |
103 |
private Reference<Project> lastFoundValue = null; |
85 |
|
104 |
|
86 |
public @Override Project getOwner(URI u) { |
105 |
/** |
87 |
return getOwner(u, uri2FileObject(u)); |
106 |
* |
88 |
} |
107 |
* #111892 |
89 |
|
108 |
*/ |
90 |
private static Project getOwner(URI u, FileObject f) { |
109 |
public void resetLastFoundReferences() { |
91 |
synchronized (SimpleFileOwnerQueryImplementation.class) { |
110 |
synchronized (this) { |
92 |
if (u.equals(lastFoundKey)) { |
111 |
lastFoundValue = null; |
93 |
Project p = lastFoundValue.get(); |
112 |
lastFoundKey = null; |
94 |
if (p != null) { |
|
|
95 |
return p; |
96 |
} |
97 |
} |
113 |
} |
98 |
} |
|
|
99 |
Project p = findOwner(u.toString(), f); |
100 |
if (p != null) { |
101 |
synchronized (SimpleFileOwnerQueryImplementation.class) { |
102 |
lastFoundKey = u; |
103 |
lastFoundValue = new WeakReference<Project>(p); |
104 |
} |
105 |
} |
106 |
return p; |
114 |
} |
107 |
} |
115 |
|
108 |
|
116 |
|
109 |
private static Project findOwner(String u, FileObject f) { |
117 |
public Project getOwner(FileObject f) { |
110 |
// Would be nice to maintain a SortedMap<String,URI> of external owners and use headMap |
118 |
while (f != null) { |
111 |
// to jump directly to the right spot. Unfortunately we also need to inspect intermediate |
119 |
synchronized (this) { |
112 |
// existing folders (if they exist on disk) to see if they are project directories. |
120 |
if (lastFoundKey != null && lastFoundKey.get() == f) { |
113 |
if (f != null && f.isFolder()) { |
121 |
Project p = lastFoundValue.get(); |
114 |
try { |
|
|
115 |
Project p = ProjectManager.getDefault().findProject(f); |
116 |
if (p != null) { |
117 |
return p; |
118 |
} |
119 |
} catch (IOException x) { |
120 |
LOG.log(Level.FINE, "Cannot load project", x); |
121 |
return null; |
122 |
} |
123 |
} |
124 |
String externalOwner = storage().get(u, null); |
125 |
if (externalOwner != null) { |
126 |
FileObject externalOwnerFolder = uri2FileObject(URI.create(externalOwner)); |
127 |
if (externalOwnerFolder != null && externalOwnerFolder.isFolder()) { |
128 |
try { |
129 |
Project p = ProjectManager.getDefault().findProject(externalOwnerFolder); |
122 |
if (p != null) { |
130 |
if (p != null) { |
123 |
return p; |
131 |
return p; |
124 |
} |
132 |
} |
|
|
133 |
} catch (IOException x) { |
134 |
LOG.log(Level.FINE, "Cannot load external owner", x); |
125 |
} |
135 |
} |
126 |
} |
136 |
} |
127 |
boolean folder = f.isFolder(); |
137 |
// Registration is stale - no such folder, no such project, or broken. |
128 |
if (folder) { |
138 |
storage().remove(u); |
129 |
Project p; |
139 |
} |
130 |
try { |
140 |
// OK, check next level up. |
131 |
p = ProjectManager.getDefault().findProject(f); |
141 |
int slash = u.lastIndexOf('/'); |
132 |
} catch (IOException e) { |
142 |
if (slash == u.length() - 1) { // folder |
133 |
// There is a project here, but we cannot load it... |
143 |
slash = u.lastIndexOf('/', slash - 1); |
134 |
if (warnedAboutBrokenProjects.add(f)) { // #60416 |
144 |
} else if (slash == -1) { // at root? |
135 |
LOG.log(Level.FINE, "Cannot load project.", e); //NOI18N |
145 |
return null; |
136 |
} |
146 |
} // else file |
137 |
return null; |
147 |
String u2 = u.substring(0, slash + 1); // file:/tmp/foo/ or file:/tmp/foo -> file:/tmp/ |
138 |
} |
148 |
FileObject f2 = null; |
139 |
if (p != null) { |
149 |
if (f != null) { |
140 |
synchronized (this) { |
150 |
f2 = f.getParent(); |
141 |
lastFoundKey = new WeakReference<FileObject>(f); |
151 |
if (f2 == null) { |
142 |
lastFoundValue = new WeakReference<Project>(p); |
152 |
// At filesystem root, no matches. |
143 |
} |
153 |
return null; |
144 |
return p; |
|
|
145 |
} |
146 |
} |
154 |
} |
147 |
|
155 |
} else { |
148 |
if (!externalOwners.isEmpty() && (folder || externalRootsIncludeNonFolders)) { |
156 |
f2 = uri2FileObject(URI.create(u2)); // may still be null |
149 |
URI externalOwnersURI = externalOwners.get(fileObject2URI(f)); |
|
|
150 |
|
151 |
if (externalOwnersURI != null) { |
152 |
FileObject externalOwner = uri2FileObject(externalOwnersURI); |
153 |
|
154 |
if (externalOwner != null && externalOwner.isValid()) { |
155 |
try { |
156 |
// Note: will be null if there is no such project. |
157 |
Project p = ProjectManager.getDefault().findProject(externalOwner); |
158 |
synchronized (this) { |
159 |
lastFoundKey = new WeakReference<FileObject>(f); |
160 |
lastFoundValue = new WeakReference<Project>(p); |
161 |
} |
162 |
return p; |
163 |
} catch (IOException e) { |
164 |
// There is a project there, but we cannot load it... |
165 |
LOG.log(Level.FINE, "Cannot load project.", e); //NOI18N |
166 |
return null; |
167 |
} |
168 |
} |
169 |
} |
170 |
} |
171 |
if (!deserializedExternalOwners.isEmpty() && (folder || externalRootsIncludeNonFolders)) { |
172 |
FileObject externalOwner = deserializedExternalOwners.get(fileObject2URI(f)); |
173 |
if (externalOwner != null && externalOwner.isValid()) { |
174 |
try { |
175 |
// Note: will be null if there is no such project. |
176 |
Project p = ProjectManager.getDefault().findProject(externalOwner); |
177 |
synchronized (this) { |
178 |
lastFoundKey = new WeakReference<FileObject>(f); |
179 |
lastFoundValue = new WeakReference<Project>(p); |
180 |
} |
181 |
return p; |
182 |
} catch (IOException e) { |
183 |
// There is a project there, but we cannot load it... |
184 |
LOG.log(Level.FINE, "Cannot load project.", e); //NOI18N |
185 |
return null; |
186 |
} |
187 |
} |
188 |
} |
189 |
|
190 |
f = f.getParent(); |
191 |
} |
157 |
} |
192 |
return null; |
158 |
return findOwner(u2, f2); |
193 |
} |
159 |
} |
194 |
|
160 |
|
195 |
/** |
161 |
private static Preferences storage() { |
196 |
* Map from external source roots to the owning project directories. |
162 |
return NbPreferences.forModule(SimpleFileOwnerQueryImplementation.class).node("externalOwners"); |
197 |
*/ |
|
|
198 |
private static final Map<URI,URI> externalOwners = |
199 |
Collections.synchronizedMap(new HashMap<URI,URI>()); |
200 |
|
201 |
private static final Map<URI,FileObject> deserializedExternalOwners = |
202 |
Collections.synchronizedMap(new HashMap<URI,FileObject>()); |
203 |
|
204 |
private static boolean externalRootsIncludeNonFolders = false; |
205 |
|
206 |
|
207 |
static void deserialize() { |
208 |
try { |
209 |
Preferences p = NbPreferences.forModule(SimpleFileOwnerQueryImplementation.class).node("externalOwners"); |
210 |
for (String name : p.keys()) { |
211 |
URL u = new URL(p.get(name, null)); |
212 |
URI i = new URI(name); |
213 |
deserializedExternalOwners.put(i, URLMapper.findFileObject(u)); |
214 |
} |
215 |
} catch (Exception ex) { |
216 |
LOG.log(Level.INFO, null, ex); |
217 |
} |
218 |
try { |
219 |
NbPreferences.forModule(SimpleFileOwnerQueryImplementation.class).node("externalOwners").removeNode(); |
220 |
} catch (BackingStoreException ex) { |
221 |
LOG.log(Level.INFO, null, ex); |
222 |
} |
223 |
} |
224 |
|
225 |
static void serialize() { |
226 |
try { |
227 |
Preferences p = NbPreferences.forModule(SimpleFileOwnerQueryImplementation.class).node("externalOwners"); |
228 |
for (URI uri : externalOwners.keySet()) { |
229 |
URI ownerURI = externalOwners.get(uri); |
230 |
p.put(uri.toString(), ownerURI.toString()); |
231 |
} |
232 |
p.sync(); // #184310 |
233 |
} catch (Exception ex) { |
234 |
LOG.log(Level.WARNING, null, ex); |
235 |
} |
236 |
|
237 |
} |
163 |
} |
238 |
|
164 |
|
239 |
/** @see FileOwnerQuery#reset */ |
165 |
/** @see FileOwnerQuery#reset */ |
240 |
public static void reset() { |
166 |
public static void reset() { |
241 |
externalOwners.clear(); |
167 |
try { |
|
|
168 |
storage().removeNode(); |
169 |
} catch (BackingStoreException x) { |
170 |
assert false : x; |
171 |
} |
172 |
} |
173 |
|
174 |
/** @see FileOwnerQuery#markExternalOwner */ |
175 |
public static void markExternalOwnerTransient(FileObject root, Project owner) { |
176 |
try { |
177 |
markExternalOwnerTransient(root.getURL().toString(), owner); |
178 |
} catch (FileStateInvalidException x) { |
179 |
throw new IllegalArgumentException(x); |
180 |
} |
181 |
} |
182 |
|
183 |
/** @see FileOwnerQuery#markExternalOwner */ |
184 |
public static void markExternalOwnerTransient(URI root, Project owner) { |
185 |
markExternalOwnerTransient(root.toString(), owner); |
186 |
} |
187 |
|
188 |
private static void markExternalOwnerTransient(String root, Project owner) { |
189 |
if (owner != null) { |
190 |
storage().put(root, fileObject2URI(owner.getProjectDirectory()).toString()); |
191 |
} else { |
192 |
storage().remove(root); |
193 |
} |
242 |
} |
194 |
} |
243 |
|
195 |
|
244 |
private static URI fileObject2URI(FileObject f) { |
196 |
private static URI fileObject2URI(FileObject f) { |
245 |
try { |
197 |
try { |
246 |
return URI.create(f.getURL().toString()); |
198 |
return URI.create(f.getURL().toString()); |
247 |
} catch (FileStateInvalidException e) { |
199 |
} catch (FileStateInvalidException x) { |
248 |
throw (IllegalArgumentException) new IllegalArgumentException(e.toString()).initCause(e); |
200 |
throw new IllegalArgumentException(x); |
249 |
} |
201 |
} |
250 |
} |
202 |
} |
251 |
|
203 |
|
252 |
/** @see FileOwnerQuery#markExternalOwner */ |
|
|
253 |
public static void markExternalOwnerTransient(FileObject root, Project owner) { |
254 |
markExternalOwnerTransient(fileObject2URI(root), owner); |
255 |
} |
256 |
|
257 |
/** @see FileOwnerQuery#markExternalOwner */ |
258 |
public static void markExternalOwnerTransient(URI root, Project owner) { |
259 |
externalRootsIncludeNonFolders |= !root.getPath().endsWith("/"); |
260 |
if (owner != null) { |
261 |
FileObject fo = owner.getProjectDirectory(); |
262 |
externalOwners.put(root, fileObject2URI(fo)); |
263 |
deserializedExternalOwners.remove(root); |
264 |
} else { |
265 |
externalOwners.remove(root); |
266 |
} |
267 |
} |
268 |
|
269 |
private static FileObject uri2FileObject(URI u) { |
204 |
private static FileObject uri2FileObject(URI u) { |
270 |
URL url; |
205 |
URL url; |
271 |
try { |
206 |
try { |
Lines 278-325
Link Here
|
278 |
return URLMapper.findFileObject(url); |
213 |
return URLMapper.findFileObject(url); |
279 |
} |
214 |
} |
280 |
|
215 |
|
281 |
private static URI goUp(URI u) { |
|
|
282 |
assert u.isAbsolute() : u; |
283 |
assert u.getFragment() == null : u; |
284 |
assert u.getQuery() == null : u; |
285 |
// XXX isn't there any easier way to do this? |
286 |
// Using getPath in the new path does not work; nbfs: URLs break. (#39613) |
287 |
// On the other hand, nbfs: URLs are not really used any more, so do we care? |
288 |
String path = u.getPath(); |
289 |
if (path == null || path.equals("/")) { // NOI18N |
290 |
return null; |
291 |
} |
292 |
String us = u.toString(); |
293 |
if (us.endsWith("/")) { // NOI18N |
294 |
us = us.substring(0, us.length() - 1); |
295 |
assert path.endsWith("/"); // NOI18N |
296 |
path = path.substring(0, path.length() - 1); |
297 |
} |
298 |
int idx = us.lastIndexOf('/'); |
299 |
assert idx != -1 : path; |
300 |
if (path.lastIndexOf('/') == 0) { |
301 |
us = us.substring(0, idx + 1); |
302 |
} else { |
303 |
us = us.substring(0, idx); |
304 |
} |
305 |
URI nue; |
306 |
try { |
307 |
nue = new URI(us); |
308 |
} catch (URISyntaxException e) { |
309 |
throw new AssertionError(e); |
310 |
} |
311 |
if (WINDOWS) { |
312 |
String pth = nue.getPath(); |
313 |
// check that path is not "/C:" or "/" |
314 |
if ((pth.length() == 3 && pth.endsWith(":")) || |
315 |
(pth.length() == 1 && pth.endsWith("/"))) { |
316 |
return null; |
317 |
} |
318 |
} |
319 |
assert nue.isAbsolute() : nue; |
320 |
assert u.toString().startsWith(nue.toString()) : "not a parent: " + nue + " of " + u; |
321 |
return nue; |
322 |
} |
323 |
private static final boolean WINDOWS = Utilities.isWindows(); |
324 |
|
325 |
} |
216 |
} |