/* * Copyright (C) 2012-2016 NS Solutions Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package com.htmlhifive.tools.jslint.library; import java.io.File; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.commons.lang.ArrayUtils; import org.apache.commons.lang.StringUtils; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResourceVisitor; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.wst.jsdt.core.IJavaScriptProject; import org.eclipse.wst.jsdt.core.JavaScriptCore; import org.eclipse.wst.jsdt.core.JavaScriptModelException; import org.eclipse.wst.jsdt.core.compiler.CharOperation; import com.htmlhifive.tools.jslint.JSLintPluginConstant; import com.htmlhifive.tools.jslint.logger.JSLintPluginLogger; import com.htmlhifive.tools.jslint.logger.JSLintPluginLoggerFactory; import com.htmlhifive.tools.jslint.messages.Messages; /** * * プロジェクトのライブラリを管理するクラス.<br> * JavaScriptプロジェクトに設定されたインクルード情報の読み込みをする. * * @author NS Solutions Corporation * */ public final class LibraryManager { /** * ロガー. */ private static JSLintPluginLogger logger = JSLintPluginLoggerFactory.getLogger(LibraryManager.class); /** * プロジェクトとそのプロジェクトに紐づいたクラスパス群. */ private static Map<IJavaScriptProject, LibraryManager> projectLib = new HashMap<IJavaScriptProject, LibraryManager>(); /** * javaScriptプロジェクト. */ private IJavaScriptProject project; /** * ライブラリに考慮しないライブラリのID. */ private Set<String> ignoreRawIdSet; /** * コンストラクタ. * * @param project javaScriptプロジェクト. */ private LibraryManager(IJavaScriptProject project) { this.project = project; projectLib.put(project, this); ignoreRawIdSet = new HashSet<String>(); ignoreRawIdSet.add("system.js"); ignoreRawIdSet.add("dom5.js"); ignoreRawIdSet.add("browserWindow.js"); ignoreRawIdSet.add("baseBrowserLibrary.js"); ignoreRawIdSet.add("xhr.js"); } /** * インスタンスを取得する. * * @param project jsプロジェクト. * @return jsプロジェクトのライブラリマネージャインスタンス */ public static LibraryManager getInstance(IJavaScriptProject project) { LibraryManager manager = projectLib.get(project); if (manager == null) { manager = new LibraryManager(project); } return manager; } /** * インクルードパスエントリを取得する. * * @return クラスパスエントリのパス群. */ public IncludePathEntryWrapper[] getIncludePathEntries() { return getIncludePathEntries(project, false); } /** * インクルードパスエントリを取得する. * * @return クラスパスエントリのパス群. */ public IncludePathEntryWrapper[] getRawIncludePathEntries() { return getIncludePathEntries(project, true); } /** * インクルードパスエントリを取得する. * * @param project jsプロジェクト. * @param raw パスを取得するか.登録されたそのままのものを取得するか. * @return インクルードパスエントリ. */ private IncludePathEntryWrapper[] getIncludePathEntries(IJavaScriptProject project, boolean raw) { try { List<IncludePathEntryWrapper> entryList; if (raw) { entryList = new ArrayList<IncludePathEntryWrapper>(Arrays.asList(IncludePathEntryWrapperFactory .getEntryWrappers(project.getRawIncludepath()))); } else { Set<IncludePathEntryWrapper> removeIndex = getRemoveList(); entryList = new ArrayList<IncludePathEntryWrapper>(Arrays.asList(IncludePathEntryWrapperFactory .getEntryWrappers(project.getResolvedIncludepath(true)))); entryList.removeAll(removeIndex); } return (IncludePathEntryWrapper[]) entryList.toArray(new IncludePathEntryWrapper[entryList.size()]); } catch (JavaScriptModelException e) { logger.put(Messages.EM0100, e); return null; } } /** * ライブラリマネージャに紐づいたエントリで、無視対象のエントリを取得する. * * @return 無視対象のエントリ * @throws JavaScriptModelException エントリの取得失敗. */ private Set<IncludePathEntryWrapper> getRemoveList() throws JavaScriptModelException { Set<IncludePathEntryWrapper> removeSet = new HashSet<IncludePathEntryWrapper>(); IncludePathEntryWrapper[] entrys = IncludePathEntryWrapperFactory.getEntryWrappers(project .getResolvedIncludepath(true)); for (int i = 0; i < entrys.length; i++) { IncludePathEntryWrapper entry = entrys[i]; if (StringUtils.endsWithAny(entry.getPath().toString(), (String[]) ignoreRawIdSet.toArray(new String[ignoreRawIdSet.size()]))) { removeSet.add(entry); } } return removeSet; } /** * プロジェクトに紐づいたJavaScriptライブラリのパスを取得する. * * @return ワークスペース内のパス. */ public IFile[] getInternalLibPaths() { List<IPath> pathList = new ArrayList<IPath>(); addLibPaths(pathList, project); List<IFile> fileList = new ArrayList<IFile>(); for (IPath iPath : pathList) { IResource resource = ResourcesPlugin.getWorkspace().getRoot().findMember(iPath); if (resource != null) { fileList.add((IFile) resource); } } return (IFile[]) fileList.toArray(new IFile[fileList.size()]); } /** * 外部指定されているライブラリファイルを取得する. * * @param 対象プロジェクト * @return ワークスペース外のライブラリ.jsファイル */ public File[] getExternalLibFiles() { List<IPath> pathList = new ArrayList<IPath>(); addLibPaths(pathList, project); List<File> fileList = new ArrayList<File>(); for (IPath iPath : pathList) { if (!(ResourcesPlugin.getWorkspace().getRoot().exists(iPath))) { fileList.add(iPath.toFile()); } } return (File[]) fileList.toArray(new File[fileList.size()]); } /** * 引数のリストに、プロジェクトに紐づいたインクルードパス(ライブラリ)を追加する. * * @param pathList 追加されるパスリスト. * @param project pathListに追加するパスを保持したプロジェクト. */ private void addLibPaths(final List<IPath> pathList, IJavaScriptProject project) { IncludePathEntryWrapper[] entries = getIncludePathEntries(project, false); for (IncludePathEntryWrapper iEntries : entries) { if (IncludePathEntryWrapper.CPE_PROJECT == iEntries.getEntryKind()) { addLibPaths( pathList, JavaScriptCore.create((IProject) ResourcesPlugin.getWorkspace().getRoot() .findMember(iEntries.getPath()))); } else if (IncludePathEntryWrapper.CPE_LIBRARY == iEntries.getEntryKind()) { // リソースが.jsファイルかまたはフォルダの場合 IPath path = iEntries.getPath(); if (StringUtils.equals(path.getFileExtension(), JSLintPluginConstant.EXTENTION_JS)) { // ライブラリが.jsファイル pathList.add(path); } else { // ライブラリがフォルダ. IContainer libFolder = (IContainer) ResourcesPlugin.getWorkspace().getRoot().findMember(path); try { libFolder.accept(new IResourceVisitor() { @Override public boolean visit(IResource resource) throws CoreException { if (StringUtils.equals(resource.getFileExtension(), // JSLintPluginConstant.EXTENTION_JS)) { // ライブラリが.jsファイル pathList.add(resource.getFullPath()); } return true; } }); } catch (CoreException e) { logger.put(Messages.EM0100, e); } } } } } /** * 対象ソースファイルを取得する. * * @return ソースファイル群. */ public IFile[] getSourceFiles() { IncludePathEntryWrapper[] entries = getIncludePathEntries(project, false); final List<IFile> fileList = new ArrayList<IFile>(); for (IncludePathEntryWrapper iEntry : entries) { if (IncludePathEntryWrapper.CPE_SOURCE == iEntry.getEntryKind()) { IResource srcDir = ResourcesPlugin.getWorkspace().getRoot().findMember(iEntry.getPath()); if (null != srcDir) { final IPath[] includePaths = iEntry.getFullInclusionPatterns(); final IPath[] excludePaths = iEntry.getFullExclusionPatterns(); try { srcDir.accept(new IResourceVisitor() { @Override public boolean visit(IResource resource) throws CoreException { if (resource instanceof IFile) { if (!StringUtils.equals(resource.getFileExtension(), JSLintPluginConstant.EXTENTION_JS)) { return true; } if (null == includePaths || includePaths.length == 0) { fileList.add((IFile) resource); } else { includeCheck: for (IPath iPath : includePaths) { char[] include = iPath.toString().toCharArray(); if (CharOperation.pathMatch(include, resource.getFullPath().toString() .toCharArray(), true, IPath.SEPARATOR)) { fileList.add((IFile) resource); break includeCheck; } } } if (!(null == excludePaths || excludePaths.length == 0)) { excludeCheck: for (IPath iPath : excludePaths) { char[] exclude = iPath.toString().toCharArray(); if (CharOperation.pathMatch(exclude, resource.getFullPath().toString() .toCharArray(), true, IPath.SEPARATOR)) { fileList.remove((IFile) resource); break excludeCheck; } } } } return true; } }); } catch (CoreException e) { logger.put(Messages.EM0100, e); } } } } return (IFile[]) fileList.toArray(new IFile[fileList.size()]); } /** * 無視するライブラリのIDをプロジェクトに追加する. * * @param fileName 無視するライブラリファイル名. */ public void addIgnoreName(String fileName) { ignoreRawIdSet.add(fileName); } /** * ソースフォルダ内でチェック対象外のファイルを取得する. * * @return チェック対象外のファイル. */ public IFile[] getExcludeSourceFiles() { IncludePathEntryWrapper[] entries = getIncludePathEntries(project, false); final List<IFile> excludeFileList = new ArrayList<IFile>(); for (IncludePathEntryWrapper iEntry : entries) { if (IncludePathEntryWrapper.CPE_SOURCE == iEntry.getEntryKind()) { IResource srcDir = ResourcesPlugin.getWorkspace().getRoot().findMember(iEntry.getPath()); if (null != srcDir) { final IPath[] includePaths = iEntry.getFullInclusionPatterns(); final IPath[] excludePaths = iEntry.getFullExclusionPatterns(); try { srcDir.accept(new IResourceVisitor() { @Override public boolean visit(IResource resource) throws CoreException { if (resource instanceof IFile) { if (!StringUtils.equals(resource.getFileExtension(), JSLintPluginConstant.EXTENTION_JS)) { return true; } if (!(null == includePaths || includePaths.length == 0)) { for (IPath iPath : includePaths) { char[] include = iPath.toString().toCharArray(); if (!CharOperation.pathMatch(include, resource.getFullPath().toString() .toCharArray(), true, IPath.SEPARATOR)) { excludeFileList.add((IFile) resource); return true; } } } if (!(null == excludePaths || excludePaths.length == 0)) { for (IPath iPath : excludePaths) { char[] exclude = iPath.toString().toCharArray(); if (CharOperation.pathMatch(exclude, // resource.getFullPath().toString().toCharArray(), // true, IPath.SEPARATOR)) { excludeFileList.add((IFile) resource); return true; } } } } return true; } }); } catch (CoreException e) { logger.put(Messages.EM0100, e); } } } } return (IFile[]) excludeFileList.toArray(new IFile[excludeFileList.size()]); } /** * 引数のファイルが検査対象であるかどうかをチェックする. * * @param iFile 比較ファイル * @return ファイルが検査対象であればtrue,そうでない場合はfalse */ public boolean isTargetFile(IFile iFile) { return ArrayUtils.contains(getSourceFiles(), iFile); } }