1 /******************************************************************************* 2 * Copyright (c) 2015, 2019 IBM Corporation and others. 3 * 4 * This program and the accompanying materials 5 * are made available under the terms of the Eclipse Public License 2.0 6 * which accompanies this distribution, and is available at 7 * https://www.eclipse.org/legal/epl-2.0/ 8 * 9 * SPDX-License-Identifier: EPL-2.0 10 * 11 * Contributors: 12 * IBM Corporation - initial API and implementation 13 *******************************************************************************/ 14 package org.eclipse.jdt.internal.core; 15 16 import java.io.IOException; 17 import java.nio.file.FileVisitResult; 18 import java.nio.file.Path; 19 import java.nio.file.attribute.BasicFileAttributes; 20 import java.util.ArrayList; 21 import java.util.Arrays; 22 23 import org.eclipse.core.resources.IResource; 24 import org.eclipse.core.runtime.IPath; 25 import org.eclipse.core.runtime.IStatus; 26 import org.eclipse.jdt.core.IClasspathAttribute; 27 import org.eclipse.jdt.core.IClasspathEntry; 28 import org.eclipse.jdt.core.IJavaModelStatusConstants; 29 import org.eclipse.jdt.core.IModuleDescription; 30 import org.eclipse.jdt.core.JavaModelException; 31 import org.eclipse.jdt.core.compiler.CharOperation; 32 import org.eclipse.jdt.internal.compiler.env.IModule; 33 import org.eclipse.jdt.internal.compiler.env.IModulePathEntry; 34 import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; 35 import org.eclipse.jdt.internal.compiler.util.JRTUtil; 36 import org.eclipse.jdt.internal.core.util.HashtableOfArrayToObject; 37 import org.eclipse.jdt.internal.core.util.Util; 38 39 /** 40 * A package fragment root that corresponds to a module in a JRT file system. 41 * 42 * @see org.eclipse.jdt.core.IPackageFragmentRoot 43 * @see org.eclipse.jdt.internal.core.JarPackageFragmentRootInfo 44 */ 45 public class JrtPackageFragmentRoot extends JarPackageFragmentRoot implements IModulePathEntry { 46 47 String moduleName; 48 49 public static final ThreadLocal<Boolean> workingOnOldClasspath = new ThreadLocal<>(); 50 51 /** 52 * Constructs a package fragment root which represents a module 53 * contained in a JRT. 54 */ JrtPackageFragmentRoot(IPath jrtPath, String moduleName, JavaProject project, IClasspathAttribute[] extraAttributes)55 protected JrtPackageFragmentRoot(IPath jrtPath, String moduleName, JavaProject project, IClasspathAttribute[] extraAttributes) { 56 super(null, jrtPath, project, extraAttributes); 57 this.moduleName = moduleName; 58 } 59 60 @Override computeChildren(OpenableElementInfo info, IResource underlyingResource)61 protected boolean computeChildren(OpenableElementInfo info, IResource underlyingResource) throws JavaModelException { 62 final HashtableOfArrayToObject rawPackageInfo = new HashtableOfArrayToObject(); 63 final String compliance = CompilerOptions.VERSION_1_8; // TODO: Java 9 Revisit 64 65 // always create the default package 66 rawPackageInfo.put(CharOperation.NO_STRINGS, new ArrayList[] { EMPTY_LIST, EMPTY_LIST }); 67 68 try { 69 org.eclipse.jdt.internal.compiler.util.JRTUtil.walkModuleImage(this.jarPath.toFile(), 70 new org.eclipse.jdt.internal.compiler.util.JRTUtil.JrtFileVisitor<Path>() { 71 @Override 72 public FileVisitResult visitPackage(Path dir, Path mod, BasicFileAttributes attrs) throws IOException { 73 initRawPackageInfo(rawPackageInfo, dir.toString(), true, compliance); 74 return FileVisitResult.CONTINUE; 75 } 76 77 @Override 78 public FileVisitResult visitFile(Path path, Path mod, BasicFileAttributes attrs) throws IOException { 79 initRawPackageInfo(rawPackageInfo, path.toString(), false, compliance); 80 return FileVisitResult.CONTINUE; 81 } 82 83 @Override 84 public FileVisitResult visitModule(Path path, String name) throws IOException { 85 if (!JrtPackageFragmentRoot.this.moduleName.equals(name)) { 86 return FileVisitResult.SKIP_SUBTREE; 87 } 88 return FileVisitResult.CONTINUE; 89 } 90 }, JRTUtil.NOTIFY_ALL); 91 } catch (IOException e) { 92 Util.log(IStatus.ERROR, "Error reading modules" + toStringWithAncestors()); //$NON-NLS-1$ 93 } 94 95 info.setChildren(createChildren(rawPackageInfo)); 96 ((JarPackageFragmentRootInfo) info).rawPackageInfo = rawPackageInfo; 97 return true; 98 } 99 @Override createSourceMapper(IPath sourcePath, IPath rootPath)100 SourceMapper createSourceMapper(IPath sourcePath, IPath rootPath) throws JavaModelException { 101 IClasspathEntry entry = ((JavaProject) getParent()).getClasspathEntryFor(getPath()); 102 String encoding = (entry== null) ? null : ((ClasspathEntry) entry).getSourceAttachmentEncoding(); 103 IModule mod = getModule(); 104 String modName = mod == null ? null : new String(mod.name()); 105 SourceMapper mapper = new SourceMapper( 106 sourcePath, 107 rootPath == null ? modName : rootPath.toOSString(), 108 getJavaProject().getOptions(true),// cannot use workspace options if external jar is 1.5 jar and workspace options are 1.4 options 109 encoding); 110 return mapper; 111 } 112 @Override equals(Object o)113 public boolean equals(Object o) { 114 if (this == o) 115 return true; 116 if (o instanceof JrtPackageFragmentRoot) { 117 JrtPackageFragmentRoot other= (JrtPackageFragmentRoot) o; 118 return this.moduleName.equals(other.moduleName) && 119 this.jarPath.equals(other.jarPath) && 120 Arrays.equals(this.extraAttributes, other.extraAttributes); 121 } 122 return false; 123 } 124 @Override getElementName()125 public String getElementName() { 126 return this.moduleName; 127 } 128 @Override getPackageFragment(String[] pkgName)129 public PackageFragment getPackageFragment(String[] pkgName) { 130 // NOTE: Do we need a different kind of package fragment? 131 return new JarPackageFragment(this, pkgName); 132 } 133 @Override hashCode()134 public int hashCode() { 135 return this.jarPath.hashCode() + this.moduleName.hashCode() + Arrays.hashCode(this.extraAttributes); 136 } 137 @Override toStringInfo(int tab, StringBuffer buffer, Object info, boolean showResolvedInfo)138 protected void toStringInfo(int tab, StringBuffer buffer, Object info, boolean showResolvedInfo) { 139 buffer.append(tabString(tab)); 140 buffer.append("<module:").append(this.moduleName).append(">"); //$NON-NLS-1$ //$NON-NLS-2$ 141 if (info == null) { 142 buffer.append(" (not open)"); //$NON-NLS-1$ 143 } 144 } 145 146 @Override getModule()147 public IModule getModule() { 148 IModuleDescription desc = getModuleDescription(); 149 if (desc != null) { 150 try { 151 return (IModule)((JavaElement) desc).getElementInfo(); 152 } catch (JavaModelException e) { 153 Util.log(e); 154 } 155 } 156 return null; 157 } 158 159 @Override isComplianceJava9OrHigher()160 protected boolean isComplianceJava9OrHigher() { 161 return true; 162 } 163 164 @Override getModulesDeclaringPackage(String qualifiedPackageName, String requestedModuleName)165 public char[][] getModulesDeclaringPackage(String qualifiedPackageName, String requestedModuleName) { 166 if (requestedModuleName != null && !requestedModuleName.equals(this.moduleName)) 167 return null; 168 if (getPackageFragment(qualifiedPackageName).exists()) { 169 return new char[][] { requestedModuleName.toCharArray() }; 170 } 171 return null; 172 } 173 @Override ignoreErrorStatus(IStatus status)174 protected boolean ignoreErrorStatus(IStatus status) { 175 if (status.getCode() == IJavaModelStatusConstants.ELEMENT_NOT_ON_CLASSPATH 176 && workingOnOldClasspath.get() == Boolean.TRUE) 177 return true; 178 return false; 179 } 180 } 181