From 5638a8eeb68c5593b0ddac98074247a675f586b2 Mon Sep 17 00:00:00 2001 From: Chris Recoskie Date: Thu, 5 Apr 2007 19:07:10 +0000 Subject: [PATCH] ASSIGNED - bug 151850: allow user to specify which parser/language to parse a given content type with https://bugs.eclipse.org/bugs/show_bug.cgi?id=151850 Combined patch from myself and Jason Montojo --- .../cdt/core/language/AllLanguageTests.java | 29 +++ .../language/LanguageInheritanceTests.java | 200 ++++++++++++++ .../core/suite/AutomatedIntegrationSuite.java | 2 + .../ProjectLanguageConfiguration.java | 4 +- .../cdt/core/model/LanguageManager.java | 84 +++--- .../core/language/LanguageMapping.java | 26 ++ .../language/LanguageMappingResolver.java | 108 ++++++++ .../core/language/LanguageMappingStore.java | 167 +++++++++--- .../pdom/LanguageMappingChangeListener.java | 103 ++++++++ .../cdt/internal/core/pdom/PDOMManager.java | 3 + .../cdt/core/CCorePreferenceConstants.java | 10 + .../core/CCorePreferenceInitializer.java | 1 + core/org.eclipse.cdt.ui/plugin.xml | 16 ++ .../ui/language/ContentTypeMappingDialog.java | 1 + .../FileLanguageMappingPropertyPage.java | 199 ++++++++++++++ .../language/LanguageMappingLinkListener.java | 46 ++++ .../ui/language/LanguageMappingWidget.java | 243 ++++++++++++++++++ .../ProjectLanguageMappingPropertyPage.java | 202 +++++---------- ...orkspaceLanguageMappingPreferencePage.java | 73 ++++++ .../ui/preferences/PreferencesMessages.java | 13 + .../PreferencesMessages.properties | 13 + 21 files changed, 1329 insertions(+), 214 deletions(-) create mode 100644 core/org.eclipse.cdt.core.tests/model/org/eclipse/cdt/core/language/AllLanguageTests.java create mode 100644 core/org.eclipse.cdt.core.tests/model/org/eclipse/cdt/core/language/LanguageInheritanceTests.java create mode 100644 core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/language/LanguageMapping.java create mode 100644 core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/language/LanguageMappingResolver.java create mode 100644 core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/LanguageMappingChangeListener.java create mode 100644 core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/language/FileLanguageMappingPropertyPage.java create mode 100644 core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/language/LanguageMappingLinkListener.java create mode 100644 core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/language/LanguageMappingWidget.java create mode 100644 core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/language/WorkspaceLanguageMappingPreferencePage.java diff --git a/core/org.eclipse.cdt.core.tests/model/org/eclipse/cdt/core/language/AllLanguageTests.java b/core/org.eclipse.cdt.core.tests/model/org/eclipse/cdt/core/language/AllLanguageTests.java new file mode 100644 index 00000000000..d251291b662 --- /dev/null +++ b/core/org.eclipse.cdt.core.tests/model/org/eclipse/cdt/core/language/AllLanguageTests.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (c) 2007 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 + *******************************************************************************/ +package org.eclipse.cdt.core.language; + +import org.eclipse.cdt.internal.index.tests.IndexTests; +import junit.framework.Test; +import junit.framework.TestSuite; + +/** + * @author crecoskie + * + */ +public class AllLanguageTests extends TestSuite { + public static Test suite() { + TestSuite suite = new IndexTests(); + + suite.addTest(LanguageInheritanceTests.suite()); + + return suite; + } +} diff --git a/core/org.eclipse.cdt.core.tests/model/org/eclipse/cdt/core/language/LanguageInheritanceTests.java b/core/org.eclipse.cdt.core.tests/model/org/eclipse/cdt/core/language/LanguageInheritanceTests.java new file mode 100644 index 00000000000..2c5c99efbdc --- /dev/null +++ b/core/org.eclipse.cdt.core.tests/model/org/eclipse/cdt/core/language/LanguageInheritanceTests.java @@ -0,0 +1,200 @@ +/******************************************************************************* + * Copyright (c) 2007 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 + *******************************************************************************/ +package org.eclipse.cdt.core.language; + +import java.util.Collections; + +import junit.framework.Test; +import junit.framework.TestCase; + +import org.eclipse.cdt.core.dom.IPDOMManager; +import org.eclipse.cdt.core.dom.ast.gnu.c.GCCLanguage; +import org.eclipse.cdt.core.dom.ast.gnu.cpp.GPPLanguage; +import org.eclipse.cdt.core.model.ICProject; +import org.eclipse.cdt.core.model.ILanguage; +import org.eclipse.cdt.core.model.LanguageManager; +import org.eclipse.cdt.core.testplugin.CProjectHelper; +import org.eclipse.cdt.core.testplugin.util.BaseTestCase; +import org.eclipse.cdt.internal.core.CContentTypes; +import org.eclipse.cdt.internal.index.tests.IndexCompositeTests; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.content.IContentType; + +/** + * Tests for language inheritance computations. + */ +public class LanguageInheritanceTests extends BaseTestCase { + + private static final String BIN_FOLDER = "bin"; + private static final String FILE_NAME = "test.c"; + private static final IContentType[] EMPTY_CONTENT_TYPES = new IContentType[0]; + + private ICProject fCProject; + private IFile fFile; + private LanguageManager fManager; + private ILanguage fLanguage1; + private ILanguage fLanguage2; + + private IProject fProject; + + public static Test suite() { + return suite(LanguageInheritanceTests.class); + } + + protected void setUp() throws Exception { + String name = getClass().getName() + "_" + getName(); + fCProject = CProjectHelper.createCCProject(name , BIN_FOLDER, IPDOMManager.ID_NO_INDEXER); + + fProject = fCProject.getProject(); + fFile = fProject.getFile(FILE_NAME); + + fManager = LanguageManager.getInstance(); + fLanguage1 = fManager.getLanguage(GPPLanguage.ID); + fLanguage2 = fManager.getLanguage(GCCLanguage.ID); + + // Ensure global language mappings are cleared. + WorkspaceLanguageConfiguration config = fManager.getWorkspaceLanguageConfiguration(); + config.setWorkspaceMappings(Collections.EMPTY_MAP); + fManager.storeWorkspaceLanguageConfiguration(EMPTY_CONTENT_TYPES); + } + + protected void tearDown() throws Exception { + CProjectHelper.delete(fCProject); + } + + public void testDirectFileMapping() throws Exception { + ILanguage originalLanguage = fManager.getLanguageForFile(fFile); + assertDifferentLanguages(originalLanguage, fLanguage1); + + ProjectLanguageConfiguration config = fManager.getLanguageConfiguration(fCProject.getProject()); + config.addFileMapping(fFile, GPPLanguage.ID); + fManager.storeLanguageMappingConfiguration(fFile); + + assertSameLanguage(fLanguage1, fManager.getLanguageForFile(fFile)); + + config.removeFileMapping(fFile); + fManager.storeLanguageMappingConfiguration(fFile); + + assertSameLanguage(originalLanguage, fManager.getLanguageForFile(fFile)); + } + + public void testDirectProjectContentTypeMapping() throws Exception { + ILanguage originalLanguage = fManager.getLanguageForFile(fFile); + assertDifferentLanguages(originalLanguage, fLanguage1); + + String filename = fFile.getLocation().toString(); + IContentType contentType = CContentTypes.getContentType(fProject, filename); + ProjectLanguageConfiguration config = fManager.getLanguageConfiguration(fCProject.getProject()); + config.addContentTypeMapping(contentType.getId(), GPPLanguage.ID); + fManager.storeLanguageMappingConfiguration(fProject, EMPTY_CONTENT_TYPES); + + assertSameLanguage(fLanguage1, fManager.getLanguageForFile(fFile)); + + config.removeContentTypeMapping(contentType.getId()); + fManager.storeLanguageMappingConfiguration(fFile); + + assertSameLanguage(originalLanguage, fManager.getLanguageForFile(fFile)); + } + + public void testDirectWorkspaceContentTypeMapping() throws Exception { + ILanguage originalLanguage = fManager.getLanguageForFile(fFile); + assertDifferentLanguages(originalLanguage, fLanguage1); + + String filename = fFile.getLocation().toString(); + IContentType contentType = CContentTypes.getContentType(fProject, filename); + WorkspaceLanguageConfiguration config = fManager.getWorkspaceLanguageConfiguration(); + config.addWorkspaceMapping(contentType.getId(), GPPLanguage.ID); + fManager.storeWorkspaceLanguageConfiguration(EMPTY_CONTENT_TYPES); + + assertEquals(fLanguage1, fManager.getLanguageForFile(fFile)); + + config.removeWorkspaceMapping(contentType.getId()); + fManager.storeLanguageMappingConfiguration(fFile); + + assertEquals(originalLanguage, fManager.getLanguageForFile(fFile)); + } + + public void testOverriddenWorkspaceContentTypeMapping1() throws Exception { + ILanguage originalLanguage = fManager.getLanguageForFile(fFile); + assertDifferentLanguages(originalLanguage, fLanguage1); + + String filename = fFile.getLocation().toString(); + IContentType contentType = CContentTypes.getContentType(fProject, filename); + + // Set workspace mapping + WorkspaceLanguageConfiguration config = fManager.getWorkspaceLanguageConfiguration(); + config.addWorkspaceMapping(contentType.getId(), GPPLanguage.ID); + fManager.storeWorkspaceLanguageConfiguration(EMPTY_CONTENT_TYPES); + + // Override with project mapping + ProjectLanguageConfiguration config2 = fManager.getLanguageConfiguration(fCProject.getProject()); + config2.addContentTypeMapping(contentType.getId(), GCCLanguage.ID); + fManager.storeLanguageMappingConfiguration(fProject, EMPTY_CONTENT_TYPES); + + assertSameLanguage(fLanguage2, fManager.getLanguageForFile(fFile)); + } + + public void testOverriddenWorkspaceContentTypeMapping2() throws Exception { + ILanguage originalLanguage = fManager.getLanguageForFile(fFile); + assertDifferentLanguages(originalLanguage, fLanguage1); + + String filename = fFile.getLocation().toString(); + IContentType contentType = CContentTypes.getContentType(fProject, filename); + + // Set workspace mapping + WorkspaceLanguageConfiguration config = fManager.getWorkspaceLanguageConfiguration(); + config.addWorkspaceMapping(contentType.getId(), GPPLanguage.ID); + fManager.storeWorkspaceLanguageConfiguration(EMPTY_CONTENT_TYPES); + + // Override with file mapping + ProjectLanguageConfiguration config2 = fManager.getLanguageConfiguration(fCProject.getProject()); + config2.addFileMapping(fFile, GCCLanguage.ID); + fManager.storeLanguageMappingConfiguration(fFile); + + assertSameLanguage(fLanguage2, fManager.getLanguageForFile(fFile)); + } + + public void testOverriddenProjectContentTypeMapping() throws Exception { + ILanguage originalLanguage = fManager.getLanguageForFile(fFile); + assertDifferentLanguages(originalLanguage, fLanguage1); + + String filename = fFile.getLocation().toString(); + IContentType contentType = CContentTypes.getContentType(fProject, filename); + + // Set project mapping + ProjectLanguageConfiguration config = fManager.getLanguageConfiguration(fCProject.getProject()); + config.addContentTypeMapping(contentType.getId(), GPPLanguage.ID); + fManager.storeLanguageMappingConfiguration(fProject, EMPTY_CONTENT_TYPES); + + // Override with file mapping + ProjectLanguageConfiguration config2 = fManager.getLanguageConfiguration(fCProject.getProject()); + config2.addFileMapping(fFile, GCCLanguage.ID); + fManager.storeLanguageMappingConfiguration(fFile); + + assertSameLanguage(fLanguage2, fManager.getLanguageForFile(fFile)); + } + + protected void assertSameLanguage(ILanguage expected, ILanguage actual) { + if (expected != null) { + assertNotNull(actual); + assertEquals(expected.getId(), actual.getId()); + } else { + assertNull(actual); + } + } + + protected void assertDifferentLanguages(ILanguage language1, ILanguage language2) { + assertNotNull(language1); + assertNotNull(language2); + assertNotSame(language1.getId(), language2.getId()); + } +} diff --git a/core/org.eclipse.cdt.core.tests/suite/org/eclipse/cdt/core/suite/AutomatedIntegrationSuite.java b/core/org.eclipse.cdt.core.tests/suite/org/eclipse/cdt/core/suite/AutomatedIntegrationSuite.java index 39b16eb1b30..aa1e66eb93c 100644 --- a/core/org.eclipse.cdt.core.tests/suite/org/eclipse/cdt/core/suite/AutomatedIntegrationSuite.java +++ b/core/org.eclipse.cdt.core.tests/suite/org/eclipse/cdt/core/suite/AutomatedIntegrationSuite.java @@ -24,6 +24,7 @@ import junit.framework.TestSuite; import org.eclipse.cdt.core.cdescriptor.tests.CDescriptorTests; import org.eclipse.cdt.core.internal.errorparsers.tests.ErrorParserTests; import org.eclipse.cdt.core.internal.tests.PositionTrackerTests; +import org.eclipse.cdt.core.language.AllLanguageTests; import org.eclipse.cdt.core.model.tests.AllCoreTests; import org.eclipse.cdt.core.model.tests.BinaryTests; import org.eclipse.cdt.core.model.tests.ElementDeltaTests; @@ -69,6 +70,7 @@ public class AutomatedIntegrationSuite extends TestSuite { suite.addTest(ElementDeltaTests.suite()); suite.addTest(WorkingCopyTests.suite()); suite.addTest(PositionTrackerTests.suite()); + suite.addTest(AllLanguageTests.suite()); // TODO turning off indexer/search tests until the PDOM // settles. These'll probably have to be rewritten anyway. diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/language/ProjectLanguageConfiguration.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/language/ProjectLanguageConfiguration.java index f1c819efd4c..80da29b963f 100644 --- a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/language/ProjectLanguageConfiguration.java +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/language/ProjectLanguageConfiguration.java @@ -158,9 +158,9 @@ public class ProjectLanguageConfiguration { * Replaces the existing file-specific language mappings with the given * mappings. The given mappings should be between full paths * (String) and language ids (String) - * @param projectMappings + * @param fileMappings */ public void setFileMappings(Map/**/ fileMappings) { - fContentTypeMappings = new TreeMap(fileMappings); + fFileMappings = new TreeMap(fileMappings); } } diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/model/LanguageManager.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/model/LanguageManager.java index e75e28704d5..32555141393 100644 --- a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/model/LanguageManager.java +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/model/LanguageManager.java @@ -27,6 +27,7 @@ import org.eclipse.cdt.core.dom.ILinkage; import org.eclipse.cdt.core.language.ProjectLanguageConfiguration; import org.eclipse.cdt.core.language.WorkspaceLanguageConfiguration; import org.eclipse.cdt.internal.core.CContentTypes; +import org.eclipse.cdt.internal.core.language.LanguageMappingResolver; import org.eclipse.cdt.internal.core.language.LanguageMappingStore; import org.eclipse.cdt.internal.core.model.LanguageDescriptor; import org.eclipse.cdt.internal.core.model.TranslationUnit; @@ -67,6 +68,7 @@ public class LanguageManager { private HashMap fIdToLanguageDescriptorCache;//= new HashMap(); private HashMap fContentTypeToDescriptorListCache; private ListenerList fLanguageChangeListeners = new ListenerList(ListenerList.IDENTITY); + private WorkspaceLanguageConfiguration fWorkspaceMappings; public static LanguageManager getInstance() { if (instance == null) @@ -373,8 +375,15 @@ public class LanguageManager { * @since 4.0 */ public WorkspaceLanguageConfiguration getWorkspaceLanguageConfiguration() throws CoreException { - // TODO: Implement this. - return new WorkspaceLanguageConfiguration(); + synchronized (this) { + if (fWorkspaceMappings != null) { + return fWorkspaceMappings; + } + + LanguageMappingStore store = new LanguageMappingStore(); + fWorkspaceMappings = store.decodeWorkspaceMappings(); + return fWorkspaceMappings; + } } /** @@ -385,7 +394,20 @@ public class LanguageManager { * @since 4.0 */ public void storeWorkspaceLanguageConfiguration(IContentType[] affectedContentTypes) throws CoreException { - // TODO: Implement this. + synchronized (this) { + if (fWorkspaceMappings == null) { + return; + } + + LanguageMappingStore store = new LanguageMappingStore(); + store.storeMappings(fWorkspaceMappings); + } + + // Notify listeners that the language mappings have changed. + LanguageMappingChangeEvent event = new LanguageMappingChangeEvent(); + event.setType(LanguageMappingChangeEvent.TYPE_WORKSPACE); + event.setAffectedContentTypes(affectedContentTypes); + notifyLanguageChangeListeners(event); } /** @@ -402,8 +424,8 @@ public class LanguageManager { return mappings; } - LanguageMappingStore store = new LanguageMappingStore(project); - mappings = store.decodeMappings(); + LanguageMappingStore store = new LanguageMappingStore(); + mappings = store.decodeMappings(project); fLanguageConfigurationCache.put(project, mappings); return mappings; } @@ -419,11 +441,18 @@ public class LanguageManager { * @since 4.0 */ public void storeLanguageMappingConfiguration(IProject project, IContentType[] affectedContentTypes) throws CoreException { - ProjectLanguageConfiguration mappings = (ProjectLanguageConfiguration) fLanguageConfigurationCache.get(project); - LanguageMappingStore store = new LanguageMappingStore(project); - store.storeMappings(mappings); + synchronized (this) { + ProjectLanguageConfiguration mappings = (ProjectLanguageConfiguration) fLanguageConfigurationCache.get(project); + LanguageMappingStore store = new LanguageMappingStore(); + store.storeMappings(project, mappings); + } - // TODO: Notify listeners that the language mappings have changed. + // Notify listeners that the language mappings have changed. + LanguageMappingChangeEvent event = new LanguageMappingChangeEvent(); + event.setType(LanguageMappingChangeEvent.TYPE_PROJECT); + event.setProject(project); + event.setAffectedContentTypes(affectedContentTypes); + notifyLanguageChangeListeners(event); } /** @@ -448,7 +477,7 @@ public class LanguageManager { String contentTypeID = contentType.getId(); - return computeLanguage(project, fullPathToFile, contentTypeID); + return LanguageMappingResolver.computeLanguage(project, fullPathToFile, contentTypeID, false)[0].language; } /** @@ -492,7 +521,7 @@ public class LanguageManager { contentTypeID= ct.getId(); } - return computeLanguage(project, pathToFile.toPortableString(), contentTypeID); + return LanguageMappingResolver.computeLanguage(project, pathToFile.toPortableString(), contentTypeID, false)[0].language; } /** @@ -527,34 +556,7 @@ public class LanguageManager { contentTypeId= contentType.getId(); } - return computeLanguage(project, file.getProjectRelativePath().toPortableString(), contentTypeId); - } - - private ILanguage computeLanguage(IProject project, String filePath, String contentTypeId) throws CoreException { - ProjectLanguageConfiguration mappings = getLanguageConfiguration(project); - if (mappings != null) { - // File-level mappings - String id = mappings.getLanguageForFile(filePath); - if (id != null) { - return getLanguage(id); - } - - // Project-level mappings - id = mappings.getLanguageForContentType(contentTypeId); - if (id != null) { - return getLanguage(id); - } - } - - // Workspace mappings - WorkspaceLanguageConfiguration workspaceMappings = getWorkspaceLanguageConfiguration(); - String id = workspaceMappings.getLanguageForContentType(contentTypeId); - if (id != null) { - return getLanguage(id); - } - - // Content type mappings - return getLanguageForContentTypeID(contentTypeId); + return LanguageMappingResolver.computeLanguage(project, file.getProjectRelativePath().toPortableString(), contentTypeId, false)[0].language; } /** @@ -601,8 +603,8 @@ public class LanguageManager { IProject project = file.getProject(); synchronized (this) { ProjectLanguageConfiguration mappings = (ProjectLanguageConfiguration) fLanguageConfigurationCache.get(project); - LanguageMappingStore store = new LanguageMappingStore(project); - store.storeMappings(mappings); + LanguageMappingStore store = new LanguageMappingStore(); + store.storeMappings(project, mappings); } // Notify listeners that the language mappings have changed. diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/language/LanguageMapping.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/language/LanguageMapping.java new file mode 100644 index 00000000000..ffb2d0ff657 --- /dev/null +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/language/LanguageMapping.java @@ -0,0 +1,26 @@ +/******************************************************************************* + * Copyright (c) 2007 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 + *******************************************************************************/ +package org.eclipse.cdt.internal.core.language; + +import org.eclipse.cdt.core.model.ILanguage; + +/** + * A language mapping. + */ +public class LanguageMapping { + public ILanguage language; + public int inheritedFrom; + + public LanguageMapping(ILanguage language, int inheritedFrom) { + this.language = language; + this.inheritedFrom = inheritedFrom; + } +} diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/language/LanguageMappingResolver.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/language/LanguageMappingResolver.java new file mode 100644 index 00000000000..b9941d81517 --- /dev/null +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/language/LanguageMappingResolver.java @@ -0,0 +1,108 @@ +/******************************************************************************* + * Copyright (c) 2007 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 + *******************************************************************************/ +package org.eclipse.cdt.internal.core.language; + +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +import org.eclipse.cdt.core.language.ProjectLanguageConfiguration; +import org.eclipse.cdt.core.language.WorkspaceLanguageConfiguration; +import org.eclipse.cdt.core.model.LanguageManager; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.content.IContentType; + +/** + * Resolves the effective language for various resources such as + * files and projects. + */ +public class LanguageMappingResolver { + public static final int DEFAULT_MAPPING = 0; + public static final int WORKSPACE_MAPPING = 1; + public static final int PROJECT_MAPPING = 2; + public static final int FILE_MAPPING = 3; + + /** + * Returns the effective language for the file specified by the given path. + * If fetchAll is true all inherited language + * mappings will be returned in order of precedence. Otherwise, only the + * effective language will be returned. + * + * This method will always return at least one mapping. + * + * @param project the project that contains the given file + * @param filePath the path to the file + * @param contentTypeId the content type of the file (optional) + * @param fetchAll if true, returns all inherited language mappings. + * Otherwise, returns only the effective language. + * @return the effective language for the file specified by the given path. + * @throws CoreException + */ + public static LanguageMapping[] computeLanguage(IProject project, String filePath, String contentTypeId, boolean fetchAll) throws CoreException { + LanguageManager manager = LanguageManager.getInstance(); + List inheritedLanguages = new LinkedList(); + + if (project != null) { + ProjectLanguageConfiguration mappings = manager.getLanguageConfiguration(project); + + if (mappings != null) { + // File-level mappings + if (filePath != null) { + String id = mappings.getLanguageForFile(filePath); + if (id != null) { + inheritedLanguages.add(new LanguageMapping(manager.getLanguage(id), FILE_MAPPING)); + if (!fetchAll) { + return createLanguageMappingArray(inheritedLanguages); + } + } + } + + // Project-level mappings + String id = mappings.getLanguageForContentType(contentTypeId); + if (id != null) { + inheritedLanguages.add(new LanguageMapping(manager.getLanguage(id), PROJECT_MAPPING)); + if (!fetchAll) { + return createLanguageMappingArray(inheritedLanguages); + } + } + } + } + + // Workspace mappings + WorkspaceLanguageConfiguration workspaceMappings = manager.getWorkspaceLanguageConfiguration(); + String id = workspaceMappings.getLanguageForContentType(contentTypeId); + if (id != null) { + inheritedLanguages.add(new LanguageMapping(manager.getLanguage(id), WORKSPACE_MAPPING)); + if (!fetchAll) { + return createLanguageMappingArray(inheritedLanguages); + } + } + + // Platform mappings + IContentType contentType = Platform.getContentTypeManager().getContentType(contentTypeId); + inheritedLanguages.add(new LanguageMapping(manager.getLanguage(contentType), DEFAULT_MAPPING)); + return createLanguageMappingArray(inheritedLanguages); + } + + private static LanguageMapping[] createLanguageMappingArray(List inheritedLanguages) { + LanguageMapping[] results = new LanguageMapping[inheritedLanguages.size()]; + Iterator mappings = inheritedLanguages.iterator(); + int i = 0; + while (mappings.hasNext()) { + LanguageMapping mapping = (LanguageMapping) mappings.next(); + results[i] = mapping; + i++; + } + return results; + } +} diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/language/LanguageMappingStore.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/language/LanguageMappingStore.java index 88ed2147ba2..16cb515b056 100644 --- a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/language/LanguageMappingStore.java +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/language/LanguageMappingStore.java @@ -10,77 +10,176 @@ *******************************************************************************/ package org.eclipse.cdt.internal.core.language; +import java.io.IOException; +import java.io.StringReader; +import java.io.StringWriter; import java.util.Iterator; import java.util.Map; import java.util.TreeMap; import java.util.Map.Entry; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerConfigurationException; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.TransformerFactoryConfigurationError; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; + import org.eclipse.cdt.core.CCorePlugin; +import org.eclipse.cdt.core.CCorePreferenceConstants; import org.eclipse.cdt.core.ICDescriptor; import org.eclipse.cdt.core.language.ProjectLanguageConfiguration; +import org.eclipse.cdt.core.language.WorkspaceLanguageConfiguration; +import org.eclipse.cdt.internal.core.Util; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.Preferences; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +/** + * Serializes and deserializes language mappings to and from persistent storage. + */ public class LanguageMappingStore { private static final String LANGUAGE_MAPPING_ID = "org.eclipse.cdt.core.language.mapping"; //$NON-NLS-1$ private static final String PROJECT_MAPPINGS = "project-mappings"; //$NON-NLS-1$ - private static final String PROJECT_MAPPING = "project-mapping"; //$NON-NLS-1$ + private static final String WORKSPACE_MAPPINGS = "workspace-mappings"; //$NON-NLS-1$ + + private static final String CONTENT_TYPE_MAPPING = "content-type-mapping"; //$NON-NLS-1$ + + private static final String FILE_MAPPING = "file-mapping"; //$NON-NLS-1$ + private static final String ATTRIBUTE_PATH = "path"; //$NON-NLS-1$ + private static final String ATTRIBUTE_CONTENT_TYPE = "content-type"; //$NON-NLS-1$ private static final String ATTRIBUTE_LANGUAGE = "language"; //$NON-NLS-1$ - private IProject fProject; - - public LanguageMappingStore(IProject project) { - fProject = project; + public LanguageMappingStore() { } - public ProjectLanguageConfiguration decodeMappings() throws CoreException { + public ProjectLanguageConfiguration decodeMappings(IProject project) throws CoreException { ProjectLanguageConfiguration config = new ProjectLanguageConfiguration(); - ICDescriptor descriptor = getProjectDescription(); + ICDescriptor descriptor = getProjectDescription(project); Element rootElement = descriptor.getProjectData(LANGUAGE_MAPPING_ID); if (rootElement == null) { return config; } - config.setContentTypeMappings(decodeProjectMappings(rootElement)); + + NodeList mappingElements = rootElement.getElementsByTagName(PROJECT_MAPPINGS); + if (mappingElements.getLength() > 0) { + Element element = (Element) mappingElements.item(0); + config.setContentTypeMappings(decodeContentTypeMappings(element)); + config.setFileMappings(decodeFileMappings(element)); + } return config; } - protected ICDescriptor getProjectDescription() throws CoreException { - return CCorePlugin.getDefault().getCProjectDescription(fProject, true); + protected ICDescriptor getProjectDescription(IProject project) throws CoreException { + return CCorePlugin.getDefault().getCProjectDescription(project, true); } - private Map decodeProjectMappings(Element rootElement) throws CoreException { + private Map decodeContentTypeMappings(Element rootElement) throws CoreException { + return decodeMappings(rootElement, CONTENT_TYPE_MAPPING, ATTRIBUTE_CONTENT_TYPE, ATTRIBUTE_LANGUAGE); + } + + private Map decodeFileMappings(Element rootElement) throws CoreException { + return decodeMappings(rootElement, FILE_MAPPING, ATTRIBUTE_PATH, ATTRIBUTE_LANGUAGE); + } + + private Map decodeMappings(Element rootElement, String category, String keyName, String valueName) { Map decodedMappings = new TreeMap(); - NodeList elements = rootElement.getElementsByTagName(PROJECT_MAPPINGS); - for (int i = 0; i < elements.getLength(); i++) { - Element projectMappings = (Element) elements.item(i); - NodeList mappingElements = projectMappings.getElementsByTagName(PROJECT_MAPPING); - for (int j = 0; j < mappingElements.getLength(); j++) { - Element mapping = (Element) mappingElements.item(j); - String contentType = mapping.getAttribute(ATTRIBUTE_CONTENT_TYPE); - String language = mapping.getAttribute(ATTRIBUTE_LANGUAGE); - decodedMappings.put(contentType, language); - } + NodeList mappingElements = rootElement.getElementsByTagName(category); + for (int j = 0; j < mappingElements.getLength(); j++) { + Element mapping = (Element) mappingElements.item(j); + String key = mapping.getAttribute(keyName); + String value = mapping.getAttribute(valueName); + decodedMappings.put(key, value); } return decodedMappings; } - public void storeMappings(ProjectLanguageConfiguration config) throws CoreException { - ICDescriptor descriptor = getProjectDescription(); + public void storeMappings(IProject project, ProjectLanguageConfiguration config) throws CoreException { + ICDescriptor descriptor = getProjectDescription(project); Element rootElement = descriptor.getProjectData(LANGUAGE_MAPPING_ID); clearChildren(rootElement); - addProjectMappings(config.getContentTypeMappings(), rootElement); + + Document document = rootElement.getOwnerDocument(); + Element projectMappings = document.createElement(PROJECT_MAPPINGS); + rootElement.appendChild(projectMappings); + + addContentTypeMappings(config.getContentTypeMappings(), projectMappings); + addFileMappings(config.getFileMappings(), projectMappings); descriptor.saveProjectData(); } + public void storeMappings(WorkspaceLanguageConfiguration config) throws CoreException { + try { + // Encode mappings as XML and serialize as a String. + Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument(); + Element rootElement = doc.createElement(WORKSPACE_MAPPINGS); + doc.appendChild(rootElement); + addContentTypeMappings(config.getWorkspaceMappings(), rootElement); + Transformer serializer = createSerializer(); + DOMSource source = new DOMSource(doc); + StringWriter buffer = new StringWriter(); + StreamResult result = new StreamResult(buffer); + serializer.transform(source, result); + String encodedMappings = buffer.getBuffer().toString(); + + Preferences node = CCorePlugin.getDefault().getPluginPreferences();; + node.setValue(CCorePreferenceConstants.WORKSPACE_LANGUAGE_MAPPINGS, encodedMappings); + CCorePlugin.getDefault().savePluginPreferences(); + } catch (ParserConfigurationException e) { + throw new CoreException(Util.createStatus(e)); + } catch (TransformerException e) { + throw new CoreException(Util.createStatus(e)); + } + } + + public WorkspaceLanguageConfiguration decodeWorkspaceMappings() throws CoreException { + Preferences node = CCorePlugin.getDefault().getPluginPreferences();; + String encodedMappings = node.getString(CCorePreferenceConstants.WORKSPACE_LANGUAGE_MAPPINGS); + WorkspaceLanguageConfiguration config = new WorkspaceLanguageConfiguration(); + + if (encodedMappings == null || encodedMappings.length() == 0) { + return config; + } + + // The mappings are encoded as XML in a String so we need to parse it. + InputSource input = new InputSource(new StringReader(encodedMappings)); + try { + Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(input); + config.setWorkspaceMappings(decodeContentTypeMappings(document.getDocumentElement())); + return config; + } catch (SAXException e) { + throw new CoreException(Util.createStatus(e)); + } catch (IOException e) { + throw new CoreException(Util.createStatus(e)); + } catch (ParserConfigurationException e) { + throw new CoreException(Util.createStatus(e)); + } + } + + private Transformer createSerializer() throws CoreException { + try { + return TransformerFactory.newInstance().newTransformer(); + } catch (TransformerConfigurationException e) { + throw new CoreException(Util.createStatus(e)); + } catch (TransformerFactoryConfigurationError e) { + throw new CoreException(Util.createStatus(e)); + } + } + private void clearChildren(Element element) { Node child = element.getFirstChild(); while (child != null) { @@ -89,17 +188,23 @@ public class LanguageMappingStore { } } - private void addProjectMappings(Map mappings, Element rootElement) { + private void addMappings(Map mappings, Element rootElement, String category, String keyName, String valueName) { Document document = rootElement.getOwnerDocument(); - Element projectMappings = document.createElement(PROJECT_MAPPINGS); Iterator entries = mappings.entrySet().iterator(); while (entries.hasNext()) { Entry entry = (Entry) entries.next(); - Element mapping = document.createElement(PROJECT_MAPPING); - mapping.setAttribute(ATTRIBUTE_CONTENT_TYPE, (String) entry.getKey()); - mapping.setAttribute(ATTRIBUTE_LANGUAGE, (String) entry.getValue()); - projectMappings.appendChild(mapping); + Element mapping = document.createElement(category); + mapping.setAttribute(keyName, (String) entry.getKey()); + mapping.setAttribute(valueName, (String) entry.getValue()); + rootElement.appendChild(mapping); } - rootElement.appendChild(projectMappings); + } + + private void addContentTypeMappings(Map mappings, Element rootElement) { + addMappings(mappings, rootElement, CONTENT_TYPE_MAPPING, ATTRIBUTE_CONTENT_TYPE, ATTRIBUTE_LANGUAGE); + } + + private void addFileMappings(Map mappings, Element rootElement) { + addMappings(mappings, rootElement, FILE_MAPPING, ATTRIBUTE_PATH, ATTRIBUTE_LANGUAGE); } } diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/LanguageMappingChangeListener.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/LanguageMappingChangeListener.java new file mode 100644 index 00000000000..162bf224806 --- /dev/null +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/LanguageMappingChangeListener.java @@ -0,0 +1,103 @@ +/******************************************************************************* + * Copyright (c) 2007 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 + *******************************************************************************/ +package org.eclipse.cdt.internal.core.pdom; + +import org.eclipse.cdt.core.CCorePlugin; +import org.eclipse.cdt.core.model.CModelException; +import org.eclipse.cdt.core.model.ICElement; +import org.eclipse.cdt.core.model.ICElementDelta; +import org.eclipse.cdt.core.model.ICProject; +import org.eclipse.cdt.core.model.ILanguageMappingChangeListener; +import org.eclipse.cdt.core.model.ILanguageMappingChangeEvent; +import org.eclipse.cdt.core.model.LanguageManager; +import org.eclipse.cdt.internal.core.model.CElementDelta; +import org.eclipse.cdt.internal.core.model.CModelManager; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.CoreException; + +/** + * @author crecoskie + * + * This class handles changes in language mappings for the PDOM by reindexing the appropriate projects. + * This class is a a work in progress and will be changed soon to be smarter about the resources it reindexes. + */ +public class LanguageMappingChangeListener implements + ILanguageMappingChangeListener { + + private PDOMManager fManager; + + public LanguageMappingChangeListener(PDOMManager manager) { + fManager = manager; + LanguageManager.getInstance().registerLanguageChangeListener(this); + } + + + /* (non-Javadoc) + * @see org.eclipse.cdt.core.model.ILanguageMappingChangeListener#handleLanguageMappingChangeEvent(org.eclipse.cdt.core.model.ILanguageMappingsChangeEvent) + */ + public void handleLanguageMappingChangeEvent(ILanguageMappingChangeEvent event) { + IProject project = event.getProject(); + + CModelManager manager = CModelManager.getDefault(); + if(project != null) { + ICProject cProject = manager.getCModel().findCProject(project); + + if(cProject != null) + try { + fManager.reindex(cProject); + } catch (CoreException e) { + CCorePlugin.log(e); + } + } + + if (event.getType() == ILanguageMappingChangeEvent.TYPE_WORKSPACE) { + // For now reindex all projects. + // TODO: This should be smarter about figuring out which projects + // are potentially unaffected due to project settings + try { + ICProject[] cProjects = manager.getCModel().getCProjects(); + for(int k = 0; k < cProjects.length; k++) { + try { + fManager.reindex(cProjects[k]); + } catch (CoreException e) { + CCorePlugin.log(e); + } + } + } catch (CModelException e) { + CCorePlugin.log(e); + } + } else if (event.getType() == ILanguageMappingChangeEvent.TYPE_PROJECT) { + // For now, reindex the entire project since we don't know which + // files are affected. + try { + ICProject cProject = manager.getCModel().getCProject(event.getProject()); + fManager.reindex(cProject); + } catch (CModelException e) { + CCorePlugin.log(e); + } catch (CoreException e) { + CCorePlugin.log(e); + } + } else if (event.getType() == ILanguageMappingChangeEvent.TYPE_FILE) { + // Just reindex the affected file. + IFile file = event.getFile(); + ICProject cProject = manager.getCModel().getCProject(file); + ICElement element = manager.create(file, cProject); + CElementDelta delta = new CElementDelta(element); + delta.changed(element, ICElementDelta.F_CONTENT); + try { + fManager.changeProject(cProject, delta); + } catch (CoreException e) { + CCorePlugin.log(e); + } + } + } +} diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/PDOMManager.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/PDOMManager.java index b3335f478a4..79c1f452002 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/PDOMManager.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/PDOMManager.java @@ -40,6 +40,7 @@ import org.eclipse.cdt.core.model.CoreModel; import org.eclipse.cdt.core.model.ICElementDelta; import org.eclipse.cdt.core.model.ICProject; import org.eclipse.cdt.core.model.IElementChangedListener; +import org.eclipse.cdt.core.model.ILanguageMappingChangeListener; import org.eclipse.cdt.internal.core.CCoreInternals; import org.eclipse.cdt.internal.core.index.IIndexFragment; import org.eclipse.cdt.internal.core.index.IWritableIndex; @@ -143,6 +144,8 @@ public class PDOMManager implements IWritableIndexManager, IListener { private IElementChangedListener fCModelListener= new CModelListener(this); private IndexFactory fIndexFactory= new IndexFactory(this); private IndexProviderManager manager = new IndexProviderManager(); + + private ILanguageMappingChangeListener fLanguageChangeListener = new LanguageMappingChangeListener(this); /** * Serializes creation of new indexer, when acquiring the lock you are diff --git a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/CCorePreferenceConstants.java b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/CCorePreferenceConstants.java index 1be07179f6d..ddbfd2798ca 100644 --- a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/CCorePreferenceConstants.java +++ b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/CCorePreferenceConstants.java @@ -65,4 +65,14 @@ public class CCorePreferenceConstants { * Default absolute maximum size of the index-db in megabytes. */ public static final String DEFAULT_MAX_INDEX_DB_CACHE_SIZE_MB = "64"; //$NON-NLS-1$ + + /** + * Workspace-wide language mappings. + */ + public static final String WORKSPACE_LANGUAGE_MAPPINGS = CCorePlugin.PLUGIN_ID + ".workspaceLanguageMappings"; //$NON-NLS-1$ + + /** + * Default workspace-wide language mappings. + */ + public static final String DEFAULT_WORKSPACE_LANGUAGE_MAPPINGS = ""; //$NON-NLS-1$ } diff --git a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/core/CCorePreferenceInitializer.java b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/core/CCorePreferenceInitializer.java index 0907f914743..d5ff529f0af 100644 --- a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/core/CCorePreferenceInitializer.java +++ b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/core/CCorePreferenceInitializer.java @@ -45,6 +45,7 @@ public class CCorePreferenceInitializer extends AbstractPreferenceInitializer { defaultOptionsMap.put(CCorePreferenceConstants.INDEX_DB_CACHE_SIZE_PCT, CCorePreferenceConstants.DEFAULT_INDEX_DB_CACHE_SIZE_PCT); defaultOptionsMap.put(CCorePreferenceConstants.MAX_INDEX_DB_CACHE_SIZE_MB, CCorePreferenceConstants.DEFAULT_MAX_INDEX_DB_CACHE_SIZE_MB); + defaultOptionsMap.put(CCorePreferenceConstants.WORKSPACE_LANGUAGE_MAPPINGS, CCorePreferenceConstants.DEFAULT_WORKSPACE_LANGUAGE_MAPPINGS); // Store default values to default preferences IEclipsePreferences defaultPreferences = ((IScopeContext) new DefaultScope()).getNode(CCorePlugin.PLUGIN_ID); for (Iterator iter = defaultOptionsMap.entrySet().iterator(); iter.hasNext();) { diff --git a/core/org.eclipse.cdt.ui/plugin.xml b/core/org.eclipse.cdt.ui/plugin.xml index b7fb3cde4d1..89ff1db9896 100644 --- a/core/org.eclipse.cdt.ui/plugin.xml +++ b/core/org.eclipse.cdt.ui/plugin.xml @@ -685,6 +685,12 @@ class="org.eclipse.cdt.internal.ui.preferences.IndexerPreferencePage" id="org.eclipse.cdt.ui.preferences.IndexerPreferencePage" name="%indexerPrefName"/> + +