/* * Copyright 2003-2011 JetBrains s.r.o. * * 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 jetbrains.mps.make; import jetbrains.mps.project.MPSExtentions; import jetbrains.mps.project.SModuleOperations; import jetbrains.mps.vfs.FileSystem; import jetbrains.mps.vfs.IFile; import org.jetbrains.mps.openapi.module.SModule; import java.io.File; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; // FIXME AP refactor public class ModuleSources { private final Map<SModule, ModuleSources> myAvailableSources; private Dependencies myDependencies; private SModule myModule; private Map<String, JavaFile> myJavaFiles = new HashMap<String, JavaFile>(); private Map<String, ResourceFile> myResourceFiles = new HashMap<String, ResourceFile>(); private List<File> myFilesToDelete = new ArrayList<File>(); private List<JavaFile> myFilesToCompile = new LinkedList<JavaFile>(); private List<ResourceFile> myResourcesToCopy = new LinkedList<ResourceFile>(); /** * @param module Module with JavaModuleFacet */ ModuleSources(SModule module, Dependencies deps) { this(module, Collections.<SModule, ModuleSources>emptyMap(), deps); } /** * @param module Module with JavaModuleFacet */ ModuleSources(SModule module, Map<SModule, ModuleSources> availableSources, Dependencies deps) { myModule = module; myAvailableSources = availableSources; myDependencies = deps; collectInputFilesInfo(); collectOutputFilesInfo(); } public Collection<File> getFilesToDelete() { return myFilesToDelete; } public Collection<JavaFile> getFilesToCompile() { return myFilesToCompile; } public Collection<ResourceFile> getResourcesToCopy() { return myResourcesToCopy; } public boolean isUpToDate() { return isJavaUpToDate() && isResourcesUpToDate(); } public boolean isJavaUpToDate() { return myFilesToCompile.isEmpty(); } public boolean isResourcesUpToDate() { return myFilesToDelete.isEmpty() && myResourcesToCopy.isEmpty(); } public JavaFile getJavaFile(String fqName) { return myJavaFiles.get(fqName); } private void collectInputFilesInfo() { for (String source : SModuleOperations.getAllSourcePaths(myModule)) { File dir = new File(source); collectInput(dir, dir.list(), new StringBuilder(), new StringBuilder()); } } private void collectInput(File dir, String[] list, StringBuilder path, StringBuilder package_) { if (list == null) return; int initialLength = path.length(); for (String childName : list) { if (isIgnoredFileName(childName)) continue; File child = new File(dir, childName); if (childName.endsWith(MPSExtentions.DOT_JAVAFILE)) { long lastModified = child.lastModified(); if (lastModified > 0) { String className = childName.substring(0, childName.length() - MPSExtentions.DOT_JAVAFILE.length()); package_.setLength(initialLength); if (initialLength > 0) { package_.append('.'); } package_.append(className); String fqName = package_.toString(); myJavaFiles.put(fqName, new JavaFile(child, fqName, lastModified)); continue; } } String[] subList = child.list(); if (subList != null) { path.setLength(initialLength); package_.setLength(initialLength); if (initialLength > 0) { path.append('/'); package_.append('.'); } path.append(childName); package_.append(childName); collectInput(child, subList, path, package_); } else if (isResourceFileName(childName)) { path.setLength(initialLength); if (initialLength > 0) { path.append('/'); } path.append(childName); String childPath = path.toString(); myResourceFiles.put(childPath, new ResourceFile(child, childPath)); } } } private void collectOutputFilesInfo() { myFilesToCompile.addAll(myJavaFiles.values()); myResourcesToCopy.addAll(myResourceFiles.values()); IFile classesGen = SModuleOperations.getJavaFacet(myModule).getClassesGen(); if (classesGen == null) return; File outputDir = new File(classesGen.getPath()); collectOutput(outputDir, outputDir.list(), new StringBuilder(), new StringBuilder()); } private boolean isFileUpToDate(JavaFile javaFile, long classFileLastModified) { if (javaFile.getLastModified() >= classFileLastModified) { return false; } for (String fqName : myDependencies.getAllDependencies(javaFile.getClassName())) { final SModule module = myDependencies.getModule(fqName); if (module != null) { JavaFile file = myJavaFiles.get(fqName); if (file == null) { final ModuleSources targetModule = myAvailableSources.get(module); if (targetModule != null) { file = targetModule.getJavaFile(fqName); } } long javaFileLastModified = file != null ? file.getLastModified() : myDependencies.getJavaFileLastModified(fqName); if (javaFileLastModified == 0 || javaFileLastModified > classFileLastModified) { return false; } } } return true; } private void collectOutput(File outputDir, String[] files, StringBuilder path, StringBuilder package_) { if (files == null) return; int initialLength = path.length(); for (String childName : files) { if (isIgnoredFileName(childName)) continue; File file = new File(outputDir, childName); if (childName.endsWith(MPSExtentions.DOT_CLASSFILE)) { boolean isInnerClass = false; String containerName = childName.substring(0, childName.length() - MPSExtentions.DOT_CLASSFILE.length()); int indexOfDollar = containerName.indexOf("$"); if (indexOfDollar > 0) { containerName = containerName.substring(0, indexOfDollar); isInnerClass = true; } package_.setLength(initialLength); if (initialLength > 0) { package_.append('.'); } package_.append(containerName); String fqName = package_.toString(); JavaFile javaFile = myJavaFiles.get(fqName); if (javaFile == null) { myFilesToDelete.add(file); } else if (!isInnerClass && isFileUpToDate(javaFile, file.lastModified())) { myFilesToCompile.remove(javaFile); } continue; } String[] subList = file.list(); if (subList != null) { path.setLength(initialLength); package_.setLength(initialLength); if (initialLength > 0) { path.append('/'); package_.append('.'); } path.append(childName); package_.append(childName); collectOutput(file, subList, path, package_); } else if (isResourceFileName(childName)) { path.setLength(initialLength); if (initialLength > 0) { path.append('/'); } path.append(childName); String childPath = path.toString(); ResourceFile resourceFile = myResourceFiles.get(childPath); if (resourceFile == null) { myFilesToDelete.add(file); } else if (resourceFile.getFile().lastModified() < file.lastModified()) { myResourcesToCopy.remove(resourceFile); } } } } private boolean isIgnoredFileName(String fileName) { return FileSystem.getInstance().isFileIgnored(fileName); } private boolean isResourceFileName(String fileName) { int extPos = fileName.lastIndexOf('.'); return extPos == -1 || extPos > 0 && !fileName.endsWith(MPSExtentions.DOT_JAVAFILE) && !fileName.endsWith(MPSExtentions.DOT_CLASSFILE); } }