PageRenderTime 26ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/projects/netbeans-7.3/parsing.api/src/org/netbeans/modules/parsing/impl/indexing/FileObjectCrawler.java

https://gitlab.com/essere.lab.public/qualitas.class-corpus
Java | 455 lines | 374 code | 28 blank | 53 comment | 86 complexity | 13d4d68779e5436dd160e3a6bbef3f42 MD5 | raw file
  1. /*
  2. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  3. *
  4. * Copyright 2010 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 2008 Sun Microsystems, Inc.
  41. */
  42. package org.netbeans.modules.parsing.impl.indexing;
  43. import org.netbeans.modules.parsing.spi.indexing.SuspendStatus;
  44. import java.io.File;
  45. import java.io.IOException;
  46. import java.util.ArrayDeque;
  47. import java.util.Collection;
  48. import java.util.Comparator;
  49. import java.util.Deque;
  50. import java.util.HashMap;
  51. import java.util.HashSet;
  52. import java.util.Iterator;
  53. import java.util.Map;
  54. import java.util.Set;
  55. import java.util.TreeMap;
  56. import java.util.TreeSet;
  57. import java.util.logging.Level;
  58. import java.util.logging.Logger;
  59. import org.netbeans.api.annotations.common.NonNull;
  60. import org.netbeans.api.annotations.common.NullAllowed;
  61. import org.netbeans.api.java.classpath.ClassPath;
  62. import org.netbeans.api.queries.VisibilityQuery;
  63. import org.netbeans.modules.parsing.spi.indexing.Indexable;
  64. import org.openide.filesystems.FileObject;
  65. import org.openide.filesystems.FileUtil;
  66. /**
  67. *
  68. * @author Tomas Zezula
  69. */
  70. final class FileObjectCrawler extends Crawler {
  71. private static final char SEPARATOR = '/'; //NOI18N
  72. private static final Logger LOG = Logger.getLogger(FileObjectCrawler.class.getName());
  73. /*test*/ static Map<Pair<File,File>,Boolean> mockLinkTypes;
  74. private final FileObject root;
  75. private final ClassPath.Entry entry;
  76. private final FileObject[] files;
  77. FileObjectCrawler(
  78. @NonNull final FileObject root,
  79. final Set<? extends TimeStampAction> checkTimeStamps,
  80. @NullAllowed final ClassPath.Entry entry,
  81. @NonNull final CancelRequest cancelRequest,
  82. @NonNull final SuspendStatus suspendStatus) throws IOException {
  83. super (root.toURL(), checkTimeStamps, true, true, cancelRequest, suspendStatus);
  84. this.root = root;
  85. this.entry = entry;
  86. this.files = null;
  87. }
  88. FileObjectCrawler(
  89. @NonNull final FileObject root,
  90. @NullAllowed final FileObject[] files,
  91. final Set<? extends TimeStampAction> checkTimeStamps,
  92. @NullAllowed final ClassPath.Entry entry,
  93. @NonNull final CancelRequest cancelRequest,
  94. @NonNull final SuspendStatus suspendStatus) throws IOException {
  95. super (root.toURL(), checkTimeStamps, false, supportsAllFiles(root, files), cancelRequest, suspendStatus);
  96. this.root = root;
  97. this.entry = entry;
  98. this.files = files;
  99. }
  100. @Override
  101. protected boolean collectResources(Collection<Indexable> resources, Collection<Indexable> allResources) {
  102. boolean finished = true;
  103. final long tm1 = System.currentTimeMillis();
  104. final Stats stats = LOG.isLoggable(Level.FINE) ? new Stats() : null;
  105. if (files != null) {
  106. if (files.length > 1) {
  107. final Map<FileObject, Set<FileObject>> clusters = new HashMap<FileObject, Set<FileObject>>();
  108. final Map<FileObject, StringBuilder> relPaths = new HashMap<FileObject, StringBuilder>();
  109. NEXT_FILE: for(FileObject f : files) {
  110. FileObject parent = f.getParent();
  111. Set<FileObject> cluster = clusters.get(parent);
  112. if (cluster == null) {
  113. StringBuilder currentRelPath = getRelativePath(root, parent);
  114. for (Iterator<Map.Entry<FileObject,StringBuilder>> it = relPaths.entrySet().iterator(); it.hasNext();) {
  115. final Map.Entry<FileObject, StringBuilder> relPath = it.next();
  116. switch (getFileRelation(currentRelPath, relPath.getValue())) {
  117. case FIRST_IN_SECOND:
  118. final Set<FileObject> cs = clusters.get(relPath.getKey());
  119. for (FileObject csFile : cs) {
  120. if (csFile.isFolder() && FileUtil.isParentOf(csFile, f)) {
  121. continue NEXT_FILE;
  122. }
  123. }
  124. break;
  125. case SECOND_IN_FIRST:
  126. if (f.equals(relPath.getKey()) || FileUtil.isParentOf(f, relPath.getKey())) {
  127. clusters.remove(relPath.getKey());
  128. it.remove();
  129. }
  130. break;
  131. case UNRELATED:
  132. break;
  133. case EQUAL:
  134. default:
  135. throw new IllegalStateException();
  136. }
  137. }
  138. cluster = new HashSet<FileObject>();
  139. clusters.put(parent, cluster);
  140. relPaths.put(parent, currentRelPath);
  141. } else {
  142. for (Iterator<Map.Entry<FileObject,StringBuilder>> it = relPaths.entrySet().iterator(); it.hasNext();) {
  143. final Map.Entry<FileObject, StringBuilder> relPath = it.next();
  144. if (f.equals(relPath.getKey()) || FileUtil.isParentOf(f, relPath.getKey())) {
  145. clusters.remove(relPath.getKey());
  146. it.remove();
  147. }
  148. }
  149. }
  150. cluster.add(f);
  151. }
  152. for(FileObject parent : clusters.keySet()) {
  153. Set<FileObject> cluster = clusters.get(parent);
  154. StringBuilder relativePath = relPaths.get(parent);
  155. if (relativePath != null) {
  156. finished = collect(
  157. cluster.toArray(new FileObject[cluster.size()]),
  158. root,
  159. resources,
  160. allResources,
  161. stats,
  162. entry,
  163. createPathForRoot(root),
  164. relativePath);
  165. if (!finished) {
  166. break;
  167. }
  168. } // else invalid (eg. deleted) FileObject encountered
  169. }
  170. } else if (files.length == 1) {
  171. StringBuilder relativePath = getRelativePath(root, files[0].getParent());
  172. if (relativePath != null) {
  173. finished = collect(
  174. files,
  175. root,
  176. resources,
  177. allResources,
  178. stats,
  179. entry,
  180. createPathForRoot(root),
  181. relativePath);
  182. } // else invalid (eg. deleted) FileObject encountered
  183. }
  184. } else {
  185. finished = collect(
  186. root.getChildren(),
  187. root,
  188. resources,
  189. allResources,
  190. stats,
  191. entry,
  192. createPathForRoot(root),
  193. new StringBuilder());
  194. }
  195. final long tm2 = System.currentTimeMillis();
  196. if (LOG.isLoggable(Level.FINE)) {
  197. final String rootUrl = root.toURL().toString();
  198. LOG.log(Level.FINE, String.format("Up-to-date check of %d files under %s took %d ms", stats.filesCount, rootUrl, tm2 - tm1 )); //NOI18N
  199. if (LOG.isLoggable(Level.FINER)) {
  200. LOG.log(Level.FINER, "File extensions histogram for {0}:", rootUrl);
  201. Stats.logHistogram(Level.FINER, stats.extensions);
  202. LOG.finer("----");
  203. // mimetypes histogram is no longer available after crawling the files
  204. // LOG.log(Level.FINER, "Mime types histogram for {0}:", rootUrl);
  205. // Stats.logHistogram(Level.FINER, stats.mimeTypes);
  206. // LOG.finer("----");
  207. }
  208. LOG.log(Level.FINE,
  209. "Symlink tests took {0}ms, {1} symlinks into root found.", //NOI18N
  210. new Object[] {
  211. stats.linkCheckTime,
  212. stats.linkCount
  213. });
  214. }
  215. return finished;
  216. }
  217. private boolean collect (
  218. final @NonNull FileObject[] fos,
  219. final @NonNull FileObject root,
  220. final @NonNull Collection<Indexable> resources,
  221. final @NonNull Collection<Indexable> allResources,
  222. final @NullAllowed Stats stats,
  223. final @NullAllowed ClassPath.Entry entry,
  224. final @NonNull Deque<File> path,
  225. final @NonNull StringBuilder relativePathBuilder)
  226. {
  227. parkWhileSuspended();
  228. int parentPathEnd = relativePathBuilder.length();
  229. for (FileObject fo : fos) {
  230. //keep the same logic like in RepositoryUpdater
  231. if (isCancelled()) {
  232. return false;
  233. }
  234. if (!fo.isValid() || !isVisible(fo)) {
  235. continue;
  236. }
  237. relativePathBuilder.append(fo.getNameExt());
  238. boolean folder = fo.isFolder();
  239. if (folder) relativePathBuilder.append(SEPARATOR);
  240. String relativePath = relativePathBuilder.toString();
  241. try {
  242. if (entry != null && !entry.includes(relativePath)) {
  243. continue;
  244. }
  245. if (folder) {
  246. File dir = null;
  247. if (path.isEmpty() ||
  248. (dir=FileUtil.toFile(fo)) == null ||
  249. !isLink(dir,path, stats)) {
  250. if (dir != null) {
  251. path.addLast(dir);
  252. }
  253. try {
  254. if (!collect(fo.getChildren(), root, resources, allResources, stats, entry, path, relativePathBuilder)) {
  255. return false;
  256. }
  257. } finally {
  258. if (dir != null) {
  259. path.removeLast();
  260. }
  261. }
  262. }
  263. } else {
  264. if (stats != null) {
  265. stats.filesCount++;
  266. Stats.inc(stats.extensions, fo.getExt());
  267. }
  268. Indexable indexable = createIndexable(new FileObjectIndexable(root, relativePath));
  269. allResources.add(indexable);
  270. if (!isUpToDate(fo, relativePath)) {
  271. resources.add(indexable);
  272. }
  273. }
  274. } finally {
  275. relativePathBuilder.delete(parentPathEnd, relativePathBuilder.length());
  276. }
  277. }
  278. return true;
  279. }
  280. private StringBuilder getRelativePath(FileObject folder, FileObject fo) {
  281. String rp = FileUtil.getRelativePath(folder, fo);
  282. if (rp != null) {
  283. StringBuilder relativePath = new StringBuilder(rp);
  284. if (relativePath.length() > 0) {
  285. relativePath.append(SEPARATOR);
  286. }
  287. return relativePath;
  288. } else {
  289. return null;
  290. }
  291. }
  292. private boolean isVisible (final @NonNull FileObject fo) {
  293. try {
  294. return VisibilityQuery.getDefault().isVisible(fo);
  295. } finally {
  296. setListenOnVisibility(true);
  297. }
  298. }
  299. //Todo: Not exaclty correct. The correct implementation should find if whole root content
  300. //is covered by files. But correct implementation will be very very slow and probably no one
  301. //calls it with such params.
  302. private static boolean supportsAllFiles(final FileObject root, final FileObject... files) {
  303. for (FileObject file : files) {
  304. if (root == file) {
  305. return true;
  306. }
  307. }
  308. return false;
  309. }
  310. private static boolean isLink(
  311. @NonNull final File file,
  312. @NonNull final Deque<? extends File> path,
  313. @NullAllowed final Stats stats) {
  314. final long st = System.currentTimeMillis();
  315. boolean hasLink = false;
  316. try {
  317. final Iterator<? extends File> it = path.descendingIterator();
  318. while (it.hasNext()) {
  319. final File pathElement = it.next();
  320. if (file.getName().equals(pathElement.getName())) {
  321. try {
  322. if (mockLinkTypes != null ?
  323. mockLinkTypes.get(Pair.<File,File>of(pathElement, file)) :
  324. file.getCanonicalFile().equals(pathElement.getCanonicalFile())) {
  325. hasLink = true;
  326. break;
  327. }
  328. } catch (IOException ioe) {
  329. LOG.log(
  330. Level.INFO,
  331. "Cannot convert to cannonical files {0} and {1}", //NOI18N
  332. new Object[]{
  333. file,
  334. pathElement
  335. });
  336. LOG.log(
  337. Level.FINE,
  338. null,
  339. ioe);
  340. break;
  341. }
  342. }
  343. }
  344. return hasLink;
  345. } finally {
  346. long et = System.currentTimeMillis();
  347. if (stats != null) {
  348. stats.linkCheckTime+= (et-st);
  349. if (hasLink) {
  350. stats.linkCount++;
  351. }
  352. }
  353. }
  354. }
  355. private static Deque<File> createPathForRoot(@NonNull final FileObject root) {
  356. final Deque<File> result = new ArrayDeque<File>();
  357. File file = FileUtil.toFile(root);
  358. while (file != null) {
  359. result.addFirst(file);
  360. file = file.getParentFile();
  361. }
  362. return result;
  363. }
  364. @NonNull
  365. private static PathRelation getFileRelation (
  366. @NonNull final StringBuilder firstPath,
  367. @NonNull final StringBuilder secondPath) {
  368. final int min = Math.min(firstPath.length(),secondPath.length());
  369. for (int i=0; i<min; i++) {
  370. if (firstPath.charAt(i) != secondPath.charAt(i)) {
  371. return PathRelation.UNRELATED;
  372. }
  373. }
  374. if (firstPath.length() > secondPath.length()) {
  375. assert secondPath.length() == 0 || secondPath.charAt(secondPath.length()-1) == SEPARATOR;
  376. return PathRelation.FIRST_IN_SECOND;
  377. } else if (firstPath.length() < secondPath.length()) {
  378. assert firstPath.length() == 0 || firstPath.charAt(firstPath.length()-1) == SEPARATOR;
  379. return PathRelation.SECOND_IN_FIRST;
  380. } else {
  381. return PathRelation.EQUAL;
  382. }
  383. }
  384. private static final class Stats {
  385. public int filesCount;
  386. public long linkCheckTime;
  387. public int linkCount;
  388. public Map<String, Integer> extensions = new HashMap<String, Integer>();
  389. public Map<String, Integer> mimeTypes = new HashMap<String, Integer>();
  390. public static void inc(Map<String, Integer> m, String k) {
  391. Integer i = m.get(k);
  392. if (i == null) {
  393. m.put(k, 1);
  394. } else {
  395. m.put(k, i.intValue() + 1);
  396. }
  397. }
  398. public static void logHistogram(Level level, Map<String, Integer> data) {
  399. Map<Integer, Set<String>> sortedMap = new TreeMap<Integer, Set<String>>(REVERSE);
  400. for(String item : data.keySet()) {
  401. Integer freq = data.get(item);
  402. Set<String> items = sortedMap.get(freq);
  403. if (items == null) {
  404. items = new TreeSet<String>();
  405. sortedMap.put(freq, items);
  406. }
  407. items.add(item);
  408. }
  409. for(Integer freq : sortedMap.keySet()) {
  410. for(String item : sortedMap.get(freq)) {
  411. LOG.log(level, "{0}: {1}", new Object [] { item, freq }); //NOI18N
  412. }
  413. }
  414. }
  415. private static final Comparator<Integer> REVERSE = new Comparator<Integer>() {
  416. public int compare(Integer o1, Integer o2) {
  417. return -1 * o1.compareTo(o2);
  418. }
  419. };
  420. } // End of Stats class
  421. private enum PathRelation {
  422. UNRELATED,
  423. EQUAL,
  424. FIRST_IN_SECOND,
  425. SECOND_IN_FIRST
  426. }
  427. }