/******************************************************************************* * Copyright (c) 2005, 2010 IBM Corporation and others. * 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: * IBM Corporation - initial API and implementation * bug:244839 - eugene@genuitec.com *******************************************************************************/ package org.eclipse.wst.jsdt.internal.core; import java.io.File; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Path; import org.eclipse.wst.jsdt.core.IAccessRule; import org.eclipse.wst.jsdt.core.IIncludePathAttribute; import org.eclipse.wst.jsdt.core.IIncludePathEntry; import org.eclipse.wst.jsdt.core.IJavaScriptProject; import org.eclipse.wst.jsdt.core.IJavaScriptUnit; import org.eclipse.wst.jsdt.core.IPackageFragmentRoot; import org.eclipse.wst.jsdt.core.JSDScopeUtil; import org.eclipse.wst.jsdt.core.JavaScriptCore; import org.eclipse.wst.jsdt.core.JavaScriptModelException; import org.eclipse.wst.jsdt.core.JsGlobalScopeContainerInitializer; import org.eclipse.wst.jsdt.core.WorkingCopyOwner; import org.eclipse.wst.jsdt.core.search.IJavaScriptSearchScope; import org.eclipse.wst.jsdt.internal.compiler.env.AccessRestriction; import org.eclipse.wst.jsdt.internal.core.search.IRestrictedAccessBindingRequestor; import org.eclipse.wst.jsdt.internal.core.search.JavaSearchScope; public class DocumentContextFragmentRoot extends PackageFragmentRoot{ /* * if user includes dojo.js check if dojo.js.uncompressed.js exists instead and replace with that. */ public static final boolean HACK_DOJO= true; private final String UNCOMPRESSED_DOJO="dojo.js.uncompressed.js"; //$NON-NLS-1$ private final String DOJO_COMPRESSED = "dojo.js"; //$NON-NLS-1$ //private static final ClasspathAttribute HIDE = new ClasspathAttribute("hide","true"); //$NON-NLS-1$ //$NON-NLS-2$ private String[] includedFiles; //private Long[] timeStamps; private IFile fRelativeFile; private IResource absolutePath; private IPath webContext; private IIncludePathEntry rawClassPathEntry; //public static final boolean RETURN_CU = true; private static final boolean DEBUG = false; //private boolean dirty; private static int instances=0; private IJavaScriptUnit[] workingCopies; private String[] fSystemFiles; private RestrictedDocumentBinding importPolice; class RestrictedDocumentBinding implements IRestrictedAccessBindingRequestor { private ArrayList foundPaths=new ArrayList(); private String exclude; private boolean shown; public void reset() { foundPaths.clear(); shown=false; } public boolean acceptBinding(int type,int modifiers, char[] packageName,char[] simpleTypeName, String path, AccessRestriction access) { if(path!=null && exclude!=null && path.compareTo(exclude)==0) return false; if(DEBUG && !shown) { shown=false; IJavaScriptProject proj = getJavaScriptProject(); try { IIncludePathEntry[] entries = proj.getResolvedIncludepath(true); System.out.println("DocumentContextFragmentRoot ====>" +"Project Classpath : \n"); //$NON-NLS-1$ //$NON-NLS-2$ for(int i = 0;i<entries.length;i++) { System.out.println("\t" + entries[i].getPath()); //$NON-NLS-1$ } } catch (JavaScriptModelException ex) { // TODO Auto-generated catch block ex.printStackTrace(); } } for (int i = 0; workingCopies!=null && i < workingCopies.length; i++) { if (workingCopies[i].getPath().toString().equals(path)) { if(DEBUG) System.out.println("DocumentContextFragmentRoot ====>" +"REJECTING binding..\n\t" + new String(simpleTypeName) + " in " + path + "\n\tfor file " + fRelativeFile.toString()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ if(DEBUG) System.out.println("\tType is in WorkingCopies "); //$NON-NLS-1$ return false; } } this.foundPaths.add(path); return true; // for(int i = 0;i<includedFiles.length;i++) { // if(Util.isSameResourceString(path, includedFiles[i])) { // if(DEBUG) System.out.println("DocumentContextFragmentRoot ====>" + "Accepting binding.. " + new String(simpleTypeName) + " in " + path + "\n\tfor file " + fRelativeFile.toString()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ // this.foundPaths.add(path); // return true; // } else if(includedFiles[i].equals("*")) { //$NON-NLS-1$ // this.foundPaths.add(path); // return true; // } // else if(HACK_DOJO) { // String includeString = includedFiles[i]; // if(path.toLowerCase().indexOf(DOJO_COMPRESSED)>0 && (includeString.toLowerCase().indexOf(UNCOMPRESSED_DOJO)>0)) { // this.foundPaths.add(path); // return true; // } // // } // } // // String systemFiles[] = getProjectSystemFiles(); // // for(int i = 0;i<systemFiles.length;i++) { // if(Util.isSameResourceString(path, systemFiles[i]) || (new Path(systemFiles[i])).isPrefixOf(new Path(path))) { // if(DEBUG) System.out.println("DocumentContextFragmentRoot ====>" + "Accepting binding.. " + new String(simpleTypeName) + " in " + path + " \n\tfor file " + fRelativeFile.toString()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ // this.foundPaths.add(path); // return true; // } // } // if(DEBUG) System.out.println("DocumentContextFragmentRoot ====>" +"REJECTING binding..\n\t" + new String(simpleTypeName) + " in " + path + " \n\tfor file " + fRelativeFile.toString()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ // if(DEBUG) System.out.println("\t(relative) page includes = : " ); //$NON-NLS-1$ // if(DEBUG) { // for(int i = 0;includedFiles!=null && i<includedFiles.length;i++) { // System.out.println("\t\t" + includedFiles[i]); //$NON-NLS-1$ // } // } // //this.foundPath=null; // return false; } public String getFoundPath() { return foundPaths.size()>0?(String)foundPaths.get(0):null; } /* (non-Javadoc) * @see org.eclipse.wst.jsdt.internal.core.search.IRestrictedAccessBindingRequestor#getFoundPaths() */ public ArrayList getFoundPaths() { return foundPaths; } /* (non-Javadoc) * @see org.eclipse.wst.jsdt.internal.core.search.IRestrictedAccessBindingRequestor#setExcludePath(java.lang.String) */ public void setExcludePath(String excludePath) { this.exclude=excludePath; } } public String[] getProjectSystemFiles() { if(fSystemFiles!=null) return fSystemFiles; IJavaScriptProject javaProject = getJavaScriptProject(); int lastGood = 0; IPackageFragmentRoot[] projectRoots = null; try { projectRoots = javaProject.getPackageFragmentRoots(); for(int i =0;i<projectRoots.length;i++) { if(projectRoots[i].isLanguageRuntime()) { projectRoots[lastGood++]=projectRoots[i]; }else if(projectRoots[i].getRawIncludepathEntry().getEntryKind()== IIncludePathEntry.CPE_SOURCE) { projectRoots[lastGood++]=projectRoots[i]; } } } catch (JavaScriptModelException ex) { projectRoots = new IPackageFragmentRoot[0]; } fSystemFiles = new String[lastGood ]; for(int i = 0;i<fSystemFiles.length;i++) { fSystemFiles[i] = projectRoots[i].getPath().toString().intern(); } return fSystemFiles; } public void classpathChange() { fSystemFiles=null; } public DocumentContextFragmentRoot(IJavaScriptProject project, IFile resourceRelativeFile, IPath resourceAbsolutePath, IPath webContext, IIncludePathEntry rawClassPath) { super(resourceRelativeFile, (JavaProject)project); fRelativeFile = resourceRelativeFile ; // this.includedFiles = new IPath[0]; //this.timeStamps = new Long[0]; this.absolutePath = ((IContainer)project.getResource()).findMember(resourceAbsolutePath); this.webContext=webContext; this.rawClassPathEntry = rawClassPath; //dirty = true; if(DEBUG) System.out.println("DocumentContextFragmentRoot ====>" + "Creating instance for total of:>>" + ++instances + "<<. \n\tRelative file:" + fRelativeFile.toString()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ } public void finalize() { if(DEBUG) System.out.println("DocumentContextFragmentRoot ====>" + "finalize() for a total of:>>" + --instances + "<<. \n\tRelative file:" + fRelativeFile!=null?null:fRelativeFile.toString()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ } /* (non-Javadoc) * @see org.eclipse.wst.jsdt.internal.core.PackageFragmentRoot#getRawClasspathEntry() */ public IIncludePathEntry getRawIncludepathEntry() throws JavaScriptModelException { if(rawClassPathEntry!=null) return rawClassPathEntry; return super.getRawIncludepathEntry(); } protected RestrictedDocumentBinding getRestrictedAccessRequestor() { if(importPolice==null) { importPolice = new RestrictedDocumentBinding(); } importPolice.reset(); return importPolice; } public DocumentContextFragmentRoot(IJavaScriptProject project, IFile resourceRelativeFile, IPath resourceAbsolutePath, IPath webContext) { this(project,resourceRelativeFile,resourceAbsolutePath,webContext,null); } public DocumentContextFragmentRoot(IJavaScriptProject project, IFile resourceRelativeFile) { this(project,resourceRelativeFile, new Path(""), new Path("")); //$NON-NLS-1$ //$NON-NLS-2$ } public void setIncludedFiles2(String[] fileNames) { ArrayList newImports = new ArrayList(); //int arrayLength = 0; for(int i = 0; i<fileNames.length;i++) { File importFile = isValidImport(fileNames[i]); if(importFile==null) continue; IPath importPath = resolveChildPath(fileNames[i]); newImports.add( importPath.toString() ); } boolean equals = includedFiles!=null && newImports.size()==includedFiles.length; for(int i=0;equals && i<newImports.size();i++) { if(((String)newImports.get(i)).compareTo(includedFiles[i])!=0) equals=false; } if(DEBUG) System.out.println("DocumentContextFragmentRoot ====>" + "Imports " + (equals?"did NOT change": "CHANGED:") + "\n"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ if(DEBUG) { for(int i = 0;includedFiles!=null && i<includedFiles.length;i++) { System.out.println("\t\t" + includedFiles[i]); //$NON-NLS-1$ } } if(equals) return; /* start try and expand the include paths from the library entries if necisary */ IIncludePathEntry[] current = new IIncludePathEntry[0]; IJavaScriptProject javaProject = getJavaScriptProject(); try { current = javaProject.getRawIncludepath(); } catch (JavaScriptModelException ex) { // TODO Auto-generated catch block // ex.printStackTrace(); } for(int i = 0;i<current.length;i++) { JsGlobalScopeContainerInitializer init = JSDScopeUtil.getContainerInitializer(current[i].getPath()); for(int k=0;k<fileNames.length;k++) { String[] newEntries = init.resolvedLibraryImport(fileNames[k]); if(newEntries!=null && newEntries.length>0 ) { newImports.removeAll(Arrays.asList(newEntries)); newImports.addAll(Arrays.asList(newEntries)); } } } /* end class path expansion */ this.includedFiles = (String[])newImports.toArray(new String[newImports.size()]); // System.arraycopy(newImports, 0, this.includedFiles, 0, arrayLength); updateClasspathIfNeeded(); dojoHack(); } public void setIncludedFiles(String[] fileNames) { String[] newImports = new String[fileNames.length]; //Long[] newTimestamps = new Long[fileNames.length]; int arrayLength = 0; for(int i = 0; i<fileNames.length;i++) { File importFile = isValidImport(fileNames[i]); if(importFile==null && !fileNames[i].equals("*")) continue; //$NON-NLS-1$ if(fileNames[i].equals("*")) { newImports[arrayLength++] = fileNames[i]; } else { IPath importPath = resolveChildPath(fileNames[i]); newImports[arrayLength++] = importPath.toString(); } //newTimestamps[arrayLength] = new Long(importFile.lastModified()); //arrayLength++; } boolean equals = includedFiles!=null && arrayLength==includedFiles.length; for(int i=0;equals && i<arrayLength;i++) { if(newImports[i].compareTo(includedFiles[i])!=0) equals=false; } //this.includedFiles!=null && (newImports !=null) && this.includedFiles.length == arrayLength; //equals = equals || (this.includedFiles==null && newImports ==null); //if(!equals) removeStaleClasspath(this.includedFiles); // // // if(!equals) dirty = true; // // /* try some more cases */ // // for(int i = 0;!dirty && i<this.includedFiles.length;i++) { // if(!(this.includedFiles[i].equals(newImports[i]))) { // dirty = true; // // } // } // // for(int i = 0;!dirty && i<newTimestamps.length;i++) { // if(!(this.timeStamps[i].equals(newTimestamps[i]))) { // dirty = true; // } // } // // if(!dirty) return; if(DEBUG) System.out.println("DocumentContextFragmentRoot ====>" + "Imports " + (equals?"did NOT change": "CHANGED:") + "\n"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ if(DEBUG) { for(int i = 0;includedFiles!=null && i<includedFiles.length;i++) { System.out.println("\t\t" + includedFiles[i]); //$NON-NLS-1$ } } if(equals) return; this.includedFiles = new String[arrayLength]; // this.timeStamps = new Long[arrayLength]; System.arraycopy(newImports, 0, this.includedFiles, 0, arrayLength); // System.arraycopy(newTimestamps, 0, this.timeStamps, 0, arrayLength); dojoHack(); updateClasspathIfNeeded(); } private void dojoHack() { if(!HACK_DOJO) return; for(int i = 0;i<includedFiles.length;i++) { String includeString = includedFiles[i]; int dojoIndex = includeString.toLowerCase().indexOf(DOJO_COMPRESSED); if(includeString!=null && dojoIndex>=0) { /* found dojo.js replace it with dojo.js.uncompressed.js if it exists */ String newIncludeString = includeString.substring(0, dojoIndex) + UNCOMPRESSED_DOJO + includeString.substring(dojoIndex + DOJO_COMPRESSED.length(),includeString.length()); File djUncom = isValidImport(newIncludeString); if(djUncom!=null && djUncom.exists()) { includedFiles[i] = newIncludeString; } } } } //private void removeStaleClasspath(String[] oldEntries) { // // } private void updateClasspathIfNeeded() { ArrayList newEntriesList = new ArrayList(); IJavaScriptProject javaProject = getJavaScriptProject(); IResource myResource = getResource(); IContainer folder = (IContainer)myResource; for(int i = 0;i<includedFiles.length;i++) { IResource theFile = folder.findMember(includedFiles[i]); if(theFile == null || javaProject.isOnIncludepath(theFile)) continue; IIncludePathEntry entry = JavaScriptCore.newLibraryEntry(theFile.getLocation().makeAbsolute(), null, null, new IAccessRule[0], new IIncludePathAttribute[] {IIncludePathAttribute.HIDE}, true); newEntriesList.add(entry); } IIncludePathEntry[] current = new IIncludePathEntry[0]; try { current = javaProject.getRawIncludepath(); } catch (JavaScriptModelException ex) { // TODO Auto-generated catch block // ex.printStackTrace(); } IIncludePathEntry[] newCpEntries = new IIncludePathEntry[newEntriesList.size() + current.length]; System.arraycopy(current, 0, newCpEntries, 0, current.length); int newPtr = 0 ; for(int i = current.length; i<newCpEntries.length;i++) { newCpEntries[i] = (IIncludePathEntry)newEntriesList.get(newPtr++); } try { javaProject.setRawIncludepath(newCpEntries, false, new NullProgressMonitor()); } catch (JavaScriptModelException ex) {} } public IPath resolveChildPath(String childPathString) { // Genuitec Begin Fix 6149: Exception opening external HTML file if (getResource() == null) { return null; } // Genuitec End Fix 6149: Exception opening external HTML file /* relative paths: * ./testfile.js are relative to file scope * absolute paths: /scripts/file.js are relative to absolutePath, and must be made relative to this resource * if the file does not exist in context root, the path is the absolute path on the filesystem. */ if(childPathString==null) return null; if(childPathString.length()==0) return new Path(""); //$NON-NLS-1$ IPath resolvedPath = null; IResource member; switch(childPathString.charAt(0)) { default: resolvedPath = new Path(childPathString); //if(resolvedPath.toFile()!=null && resolvedPath.toFile().exists()) break; member = ((IContainer)getResource()).findMember(resolvedPath); if(member!=null && member.exists()) break; case '/': case '\\': IPath childPath = new Path(childPathString); IPath newPath = childPath.removeFirstSegments(childPath.matchingFirstSegments(webContext)); member = ((IContainer)getResource()).findMember(newPath); //if(member.exists()) return new Path(newPath); resolvedPath = newPath; if(member!=null && member.exists()) break; case '.': /* returns a new relative path thats relative to the resource */ IPath relative=null; try { relative = fRelativeFile.getFullPath().removeLastSegments(1); } catch (Exception ex) { /* file usually outside of workspace in this instance */ return null; } IPath relRes = getResource().getFullPath(); if(relRes.isPrefixOf(relative)) { IPath amended = relative.removeFirstSegments(relRes.matchingFirstSegments(relative)); resolvedPath = amended.append(childPathString); } break; } return resolvedPath; } public IPath getPath() { if(fRelativeFile!=null) return fRelativeFile.getFullPath().removeLastSegments(1); return super.getPath(); } public boolean equals(Object o) { // if (this == o) // return true; if (!(o instanceof DocumentContextFragmentRoot)) return false; DocumentContextFragmentRoot other= (DocumentContextFragmentRoot) o; boolean equalRelativeFileAndIncludedFileLengths = (this.fRelativeFile!=null && this.fRelativeFile.equals(other.fRelativeFile)) && this.includedFiles!=null && (other.includedFiles !=null) && this.includedFiles.length == other.includedFiles.length; if(!equalRelativeFileAndIncludedFileLengths) return false; /* try some more cases */ for(int i = 0;i<this.includedFiles.length;i++) { if(!(this.includedFiles[i].equals(other.includedFiles[i]))) return false; } // for(int i = 0;i<this.timeStamps.length;i++) { // if(!(this.timeStamps[i].equals( other.timeStamps[i] ) )) return false; // } return true; } public String getElementName() { if(fRelativeFile!=null) return this.fRelativeFile.getName(); return super.getElementName(); } public int hashCode() { return fRelativeFile!=null?this.fRelativeFile.hashCode():super.hashCode(); } public boolean isExternal() { return false; } /** * Jars and jar entries are all read only */ public boolean isReadOnly() { return false; } /** * Returns whether the corresponding resource or associated file exists */ protected boolean resourceExists() { return true; } public SearchableEnvironment newSearchableNameEnvironment(WorkingCopyOwner owner) throws JavaScriptModelException { /* previously restricted searchable environment to 'this'. But that removes library entries from search results so going back to global project */ SearchableEnvironment env = super.newSearchableNameEnvironment(owner);//new SearchableEnvironment((JavaProject)getJavaProject(),this, owner); int includeMask = IJavaScriptSearchScope.SOURCES | IJavaScriptSearchScope.APPLICATION_LIBRARIES | IJavaScriptSearchScope.SYSTEM_LIBRARIES | IJavaScriptSearchScope.REFERENCED_PROJECTS; env.nameLookup.setRestrictedAccessRequestor(getRestrictedAccessRequestor()); ((JavaSearchScope)env.searchScope).add((JavaProject)getJavaScriptProject(), includeMask, new HashSet(2)); return env; } /* * Returns a new name lookup. This name lookup first looks in the given working copies. */ public NameLookup newNameLookup(IJavaScriptUnit[] workingCopies) throws JavaScriptModelException { this.workingCopies = workingCopies; NameLookup lookup = super.newNameLookup(this.workingCopies); lookup.setRestrictedAccessRequestor(getRestrictedAccessRequestor()); return lookup; //return ((LookupScopeElementInfo)getElementInfo()).newNameLookup( workingCopies); } /* * Returns a new name lookup. This name lookup first looks in the working copies of the given owner. */ public NameLookup newNameLookup(WorkingCopyOwner owner) throws JavaScriptModelException { NameLookup lookup = super.newNameLookup(owner); lookup.setRestrictedAccessRequestor(getRestrictedAccessRequestor()); return lookup; // // JavaModelManager manager = JavaModelManager.getJavaModelManager(); // IJavaScriptUnit[] workingCopies = owner == null ? null : manager.getWorkingCopies(owner, true/*add primary WCs*/); // return newNameLookup(workingCopies); } public File isValidImport(String importName) { IPath filePath = resolveChildPath(importName); if(filePath==null) return null; File file = filePath.toFile(); if(file.isFile()) { return file; }else { // IPath childPath = new Path(importName); IFile resolved = null; /* since eclipse throws an exception if it doesn't exists (contrary to its API) we have to catch it*/ try { resolved = ((IContainer)getResource()).getFile(new Path(file.getPath())); }catch(Exception e) {} boolean exists = resolved!=null && resolved.exists(); /* Special case for absolute paths specified with \ and / */ if( importName.charAt(0)=='\\' || importName.charAt(0)=='/'){ int seg = resolved.getFullPath().matchingFirstSegments(webContext); exists = exists && (webContext!=new Path("") && seg >0); //$NON-NLS-1$ } if(exists) return new File(resolved.getLocation().toString()); } return null; } public int getKind() throws JavaScriptModelException { return IPackageFragmentRoot.K_SOURCE; } public String toString() { StringBuffer me = new StringBuffer("Relative to: " + fRelativeFile.getName() + "\n"); //$NON-NLS-1$ //$NON-NLS-2$ me.append("Absolute to: " + webContext + "\n"); //$NON-NLS-1$ //$NON-NLS-2$ me.append("Included File\t\t\tLast Moddified\n"); //$NON-NLS-1$ for(int i = 0;i<includedFiles.length;i++) { me.append(includedFiles[i] /*+ "\t\t\t\t" + timeStamps[i].longValue()*/ + "\n"); //$NON-NLS-1$ } return me.toString(); } public IResource getResource() { return absolutePath; } }