/******************************************************************************* * Copyright (c) 2013, 2015 Spring IDE Developers * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Spring IDE Developers - initial API and implementation *******************************************************************************/ package org.springframework.ide.eclipse.core.java.typehierarchy; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.Collections; import java.util.Enumeration; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.jar.JarFile; import java.util.zip.ZipEntry; /** * @author Martin Lippert * @since 3.3.0 */ public class ClasspathElementJar implements ClasspathElement { private JarFile jarFile; private String jarFileName; private Set<String> knownPackageNames; private long lastModified; public ClasspathElementJar(String jarFileName) { this.jarFileName = jarFileName; } public InputStream getStream(String fullyQualifiedClassFileName, String packageName, String classFileName) throws Exception { if (!isPackage(packageName)) return null; ZipEntry entry = jarFile.getEntry(fullyQualifiedClassFileName); if (entry != null) { return jarFile.getInputStream(entry); } return null; } public void cleanup() { synchronized(this) { if (this.jarFile != null) { try { this.jarFile.close(); } catch(IOException e) { // ignore it } this.jarFile = null; } this.knownPackageNames = null; } } public long lastModified() { if (this.lastModified == 0) this.lastModified = new File(this.jarFile.getName()).lastModified(); return this.lastModified; } private boolean isPackage(String qualifiedPackageName) { if (this.knownPackageNames != null) return this.knownPackageNames.contains(qualifiedPackageName); try { synchronized(this) { if (this.jarFile == null) { this.jarFile = new JarFile(this.jarFileName); } this.knownPackageNames = findPackageSet(); } } catch(Exception e) { this.knownPackageNames = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>()); } return this.knownPackageNames.contains(qualifiedPackageName); } private Set<String> findPackageSet() { long lastModified = lastModified(); long fileSize = new File(jarFileName).length(); PackageCacheEntry cacheEntry = (PackageCacheEntry) PackageCache.get(jarFileName); if (cacheEntry != null && cacheEntry.lastModified == lastModified && cacheEntry.fileSize == fileSize) return cacheEntry.packageSet; Set<String> packageSet = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>()); packageSet.add(""); //$NON-NLS-1$ nextEntry : for (Enumeration e = jarFile.entries(); e.hasMoreElements(); ) { String fileName = ((ZipEntry) e.nextElement()).getName(); // add the package name & all of its parent packages int last = fileName.lastIndexOf('/'); while (last > 0) { // extract the package name String packageName = fileName.substring(0, last); if (!packageSet.add(packageName)) continue nextEntry; // already existed last = packageName.lastIndexOf('/'); } } PackageCache.put(jarFileName, new PackageCacheEntry(lastModified, fileSize, packageSet)); return packageSet; } // global zip file content cache private static Map<String, PackageCacheEntry> PackageCache = new ConcurrentHashMap<String, PackageCacheEntry>(); private static class PackageCacheEntry { long lastModified; long fileSize; Set<String> packageSet; public PackageCacheEntry(long lastModified, long fileSize, Set<String> packageSet) { this.lastModified = lastModified; this.fileSize = fileSize; this.packageSet = packageSet; } } }