blob: 8db305836756d203ab0a9a5d049563e8795c3ce7 [file] [log] [blame]
/*
* Copyright 2000-2009 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 org.jetbrains.idea.eclipse.config;
import com.intellij.openapi.components.PathMacroManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.options.ConfigurationException;
import com.intellij.openapi.roots.*;
import com.intellij.openapi.roots.impl.RootModelImpl;
import com.intellij.openapi.roots.impl.storage.ClassPathStorageUtil;
import com.intellij.openapi.roots.impl.storage.ClasspathStorage;
import com.intellij.openapi.roots.impl.storage.ClasspathStorageProvider;
import com.intellij.openapi.roots.libraries.Library;
import com.intellij.openapi.util.InvalidDataException;
import com.intellij.openapi.util.WriteExternalException;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.idea.eclipse.ConversionException;
import org.jetbrains.idea.eclipse.EclipseBundle;
import org.jetbrains.idea.eclipse.EclipseXml;
import org.jetbrains.idea.eclipse.IdeaXml;
import org.jetbrains.idea.eclipse.conversion.*;
import org.jetbrains.jps.eclipse.model.JpsEclipseClasspathSerializer;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
/**
* @author Vladislav.Kaznacheev
*/
public class EclipseClasspathStorageProvider implements ClasspathStorageProvider {
public static final String DESCR = EclipseBundle.message("eclipse.classpath.storage.description");
@Override
@NonNls
public String getID() {
return JpsEclipseClasspathSerializer.CLASSPATH_STORAGE_ID;
}
@Override
@Nls
public String getDescription() {
return DESCR;
}
@Override
public void assertCompatible(final ModuleRootModel model) throws ConfigurationException {
final String moduleName = model.getModule().getName();
for (OrderEntry entry : model.getOrderEntries()) {
if (entry instanceof LibraryOrderEntry) {
final LibraryOrderEntry libraryEntry = (LibraryOrderEntry)entry;
if (libraryEntry.isModuleLevel()) {
final Library library = libraryEntry.getLibrary();
if (library == null ||
libraryEntry.getRootUrls(OrderRootType.CLASSES).length != 1 ||
library.isJarDirectory(library.getUrls(OrderRootType.CLASSES)[0])) {
throw new ConfigurationException(
"Library \'" +
entry.getPresentableName() +
"\' from module \'" +
moduleName +
"\' dependencies is incompatible with eclipse format which supports only one library content root");
}
}
}
}
if (model.getContentRoots().length == 0) {
throw new ConfigurationException("Module \'" + moduleName + "\' has no content roots thus is not compatible with eclipse format");
}
final String output = model.getModuleExtension(CompilerModuleExtension.class).getCompilerOutputUrl();
final String contentRoot = getContentRoot(model);
if (output == null ||
!StringUtil.startsWith(VfsUtilCore.urlToPath(output), contentRoot) &&
PathMacroManager.getInstance(model.getModule()).collapsePath(output).equals(output)) {
throw new ConfigurationException("Module \'" +
moduleName +
"\' output path is incompatible with eclipse format which supports output under content root only.\nPlease make sure that \"Inherit project compile output path\" is not selected");
}
}
@Override
public void detach(Module module) {
EclipseModuleManagerImpl.getInstance(module).setDocumentSet(null);
}
@Override
public ClasspathConverter createConverter(Module module) {
return new EclipseClasspathConverter(module);
}
@Override
public String getContentRoot(ModuleRootModel model) {
final VirtualFile contentRoot = EPathUtil.getContentRoot(model);
if (contentRoot != null) return contentRoot.getPath();
return model.getContentRoots()[0].getPath();
}
@Override
public void modulePathChanged(Module module, String path) {
final EclipseModuleManagerImpl moduleManager = EclipseModuleManagerImpl.getInstance(module);
if (moduleManager != null) {
moduleManager.setDocumentSet(null);
}
}
public static void registerFiles(final CachedXmlDocumentSet fileCache,
final Module module,
final String moduleRoot,
final String storageRoot) {
fileCache.register(EclipseXml.CLASSPATH_FILE, storageRoot);
fileCache.register(EclipseXml.PROJECT_FILE, storageRoot);
fileCache.register(EclipseXml.PLUGIN_XML_FILE, storageRoot);
fileCache.register(module.getName() + EclipseXml.IDEA_SETTINGS_POSTFIX, moduleRoot);
}
static CachedXmlDocumentSet getFileCache(final Module module) {
final EclipseModuleManagerImpl moduleManager = EclipseModuleManagerImpl.getInstance(module);
CachedXmlDocumentSet fileCache = moduleManager != null ? moduleManager.getDocumentSet() : null;
if (fileCache == null) {
fileCache = new CachedXmlDocumentSet(module.getProject());
if (moduleManager != null) {
moduleManager.setDocumentSet(fileCache);
}
registerFiles(fileCache, module, ClasspathStorage.getModuleDir(module), ClasspathStorage.getStorageRootFromOptions(module));
fileCache.preload();
}
return fileCache;
}
@Override
public void moduleRenamed(final Module module, String newName) {
if (ClassPathStorageUtil.getStorageType(module).equals(JpsEclipseClasspathSerializer.CLASSPATH_STORAGE_ID)) {
try {
final CachedXmlDocumentSet documentSet = getFileCache(module);
final String oldEmlName = module.getName() + EclipseXml.IDEA_SETTINGS_POSTFIX;
final String root = documentSet.getParent(oldEmlName);
final File source = new File(root, oldEmlName);
if (source.exists()) {
final File target = new File(root, newName + EclipseXml.IDEA_SETTINGS_POSTFIX);
FileUtil.rename(source, target);
LocalFileSystem.getInstance().refreshAndFindFileByIoFile(target);
}
final CachedXmlDocumentSet fileCache = getFileCache(module);
DotProjectFileHelper.saveDotProjectFile(module, fileCache.getParent(EclipseXml.PROJECT_FILE));
fileCache.delete(oldEmlName);
fileCache.register(newName + EclipseXml.IDEA_SETTINGS_POSTFIX, ClasspathStorage.getModuleDir(module));
fileCache.load(newName + EclipseXml.IDEA_SETTINGS_POSTFIX, true);
}
catch (IOException ignore) {
}
catch (JDOMException e) {
}
}
}
public static class EclipseClasspathConverter implements ClasspathConverter {
private final Module module;
private static final Logger LOG = Logger.getInstance("#" + EclipseClasspathConverter.class.getName());
public EclipseClasspathConverter(final Module module) {
this.module = module;
}
@Override
public CachedXmlDocumentSet getFileSet() {
return getFileCache(module);
}
@Override
public Set<String> getClasspath(ModifiableRootModel model, final Element element) throws IOException, InvalidDataException {
try {
final HashSet<String> usedVariables = new HashSet<String>();
final CachedXmlDocumentSet documentSet = getFileSet();
String path = documentSet.getParent(EclipseXml.PROJECT_FILE);
if (!documentSet.exists(EclipseXml.PROJECT_FILE)) {
if (!documentSet.exists(EclipseXml.CLASSPATH_FILE)) {
return usedVariables;
}
path = documentSet.getParent(EclipseXml.CLASSPATH_FILE);
}
final EclipseClasspathReader classpathReader = new EclipseClasspathReader(path, module.getProject(), null);
classpathReader.init(model);
if (documentSet.exists(EclipseXml.CLASSPATH_FILE)) {
classpathReader.readClasspath(model, new ArrayList<String>(), new ArrayList<String>(), usedVariables, new HashSet<String>(), null,
documentSet.read(EclipseXml.CLASSPATH_FILE, false).getRootElement());
}
else {
EclipseClasspathReader.setOutputUrl(model, path + "/bin");
}
final String eml = model.getModule().getName() + EclipseXml.IDEA_SETTINGS_POSTFIX;
if (documentSet.exists(eml)) {
IdeaSpecificSettings.readIDEASpecific(model, documentSet, eml);
}
else {
model.getModuleExtension(CompilerModuleExtension.class).setExcludeOutput(false);
}
((RootModelImpl)model).writeExternal(element);
return usedVariables;
}
catch (ConversionException e) {
throw new InvalidDataException(e);
}
catch (WriteExternalException e) {
throw new InvalidDataException(e);
}
catch (JDOMException e) {
throw new InvalidDataException(e);
}
}
@Override
public void setClasspath(final ModuleRootModel model) throws IOException, WriteExternalException {
try {
final Element classpathElement = new Element(EclipseXml.CLASSPATH_TAG);
final EclipseClasspathWriter classpathWriter = new EclipseClasspathWriter(model);
final CachedXmlDocumentSet fileSet = getFileSet();
Element element;
try {
element = fileSet.read(EclipseXml.CLASSPATH_FILE).getRootElement();
}
catch (Exception e) {
element = null;
}
if (element != null || model.getSourceRoots().length > 0 || model.getOrderEntries().length > 2) {
classpathWriter.writeClasspath(classpathElement, element);
fileSet.write(new Document(classpathElement), EclipseXml.CLASSPATH_FILE);
}
try {
fileSet.read(EclipseXml.PROJECT_FILE);
}
catch (Exception e) {
DotProjectFileHelper.saveDotProjectFile(module, fileSet.getParent(EclipseXml.PROJECT_FILE));
}
final Element ideaSpecific = new Element(IdeaXml.COMPONENT_TAG);
final String emlFilename = model.getModule().getName() + EclipseXml.IDEA_SETTINGS_POSTFIX;
if (IdeaSpecificSettings.writeIDEASpecificClasspath(ideaSpecific, model)) {
fileSet.write(new Document(ideaSpecific), emlFilename);
}
else {
fileSet.delete(emlFilename);
}
}
catch (ConversionException e) {
throw new WriteExternalException(e.getMessage());
}
}
}
}