/******************************************************************************* * Copyright (c) 2005, 2011 QNX Software Systems 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: * Doug Schaefer (QNX) - Initial API and implementation * Markus Schorn (Wind River Systems) * IBM Corporation - Language management feature (see Bugzilla 151850) *******************************************************************************/ package org.eclipse.cdt.core.model; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import org.eclipse.cdt.core.CCorePlugin; 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.core.settings.model.ICConfigurationDescription; 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; import org.eclipse.cdt.internal.core.pdom.dom.IPDOMLinkageFactory; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IConfigurationElement; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.ISafeRunnable; import org.eclipse.core.runtime.ListenerList; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.SafeRunner; import org.eclipse.core.runtime.content.IContentType; import org.eclipse.core.runtime.content.IContentTypeManager; /** * @noextend This interface is not intended to be extended by clients. * @noinstantiate This class is not intended to be instantiated by clients. */ public class LanguageManager { private static final String NAMESPACE_SEPARATOR = "."; //$NON-NLS-1$ private static final String LANGUAGE_EXTENSION_POINT_ID = "org.eclipse.cdt.core.language"; //$NON-NLS-1$ private static final String ELEMENT_LANGUAGE = "language"; //$NON-NLS-1$ private static final String ELEMENT_CONTENT_TYPE = "contentType"; //$NON-NLS-1$ private static final String ELEMENT_PDOM_LINKAGE_FACTORY = "pdomLinkageFactory"; //$NON-NLS-1$ private static final String ATTRIBUTE_CLASS = "class"; //$NON-NLS-1$ private static final String ATTRIBUTE_ID = "id"; //$NON-NLS-1$ private static LanguageManager instance; private Map<String, ILanguage> fLanguageCache = new HashMap<String, ILanguage>(); private Map<String, IPDOMLinkageFactory> fPDOMLinkageFactoryCache= new HashMap<String, IPDOMLinkageFactory>(); private Map<String, ILanguage> fContentTypeToLanguageCache= new HashMap<String, ILanguage>(); private Map<IProject, ProjectLanguageConfiguration> fLanguageConfigurationCache = new HashMap<IProject, ProjectLanguageConfiguration>(); private boolean fIsFullyCached; private HashMap<String, ILanguageDescriptor> fIdToLanguageDescriptorCache;//= new HashMap(); private HashMap<String, List<ILanguageDescriptor>> fContentTypeToDescriptorListCache; private ListenerList fLanguageChangeListeners = new ListenerList(ListenerList.IDENTITY); private WorkspaceLanguageConfiguration fWorkspaceMappings; public static LanguageManager getInstance() { if (instance == null) instance = new LanguageManager(); return instance; } public ILanguageDescriptor getLanguageDescriptor(String id) { Map<String, ILanguageDescriptor> map = getDescriptorCache(); return map.get(id); } private HashMap<String, ILanguageDescriptor> getDescriptorCache() { if (fIdToLanguageDescriptorCache == null) { fIdToLanguageDescriptorCache = createDescriptorCache(); } return fIdToLanguageDescriptorCache; } public ILanguageDescriptor[] getLanguageDescriptors() { HashMap<String, ILanguageDescriptor> map = getDescriptorCache(); return map.values().toArray(new ILanguageDescriptor[map.size()]); } private HashMap<String, ILanguageDescriptor> createDescriptorCache() { HashMap<String, ILanguageDescriptor> map = new HashMap<String, ILanguageDescriptor>(); IConfigurationElement[] configs= Platform.getExtensionRegistry().getConfigurationElementsFor(LANGUAGE_EXTENSION_POINT_ID); for (int j = 0; j < configs.length; ++j) { final IConfigurationElement languageElem = configs[j]; if (ELEMENT_LANGUAGE.equals(languageElem.getName())) { LanguageDescriptor lDes = new LanguageDescriptor(languageElem); map.put(lDes.getId(), lDes); } } return map; } private HashMap<String, List<ILanguageDescriptor>> getContentTypeToDescriptorCache() { if (fContentTypeToDescriptorListCache == null) { fContentTypeToDescriptorListCache = createContentTypeToDescriptorCache(); } return fContentTypeToDescriptorListCache; } public Map<String, ILanguageDescriptor[]> getContentTypeIdToLanguageDescriptionsMap() { HashMap<String, ILanguageDescriptor[]> map = new HashMap<String, ILanguageDescriptor[]>(); Map<String, List<ILanguageDescriptor>> cache = getContentTypeToDescriptorCache(); for (Entry<String, List<ILanguageDescriptor>> entry : cache.entrySet()) { List<ILanguageDescriptor> list = entry.getValue(); if (list.size() > 0) { ILanguageDescriptor[] dess = list.toArray(new ILanguageDescriptor[list.size()]); map.put(entry.getKey(), dess); } } return map; } private HashMap<String, List<ILanguageDescriptor>> createContentTypeToDescriptorCache() { HashMap<String, List<ILanguageDescriptor>> map = new HashMap<String, List<ILanguageDescriptor>>(); Map<String, ILanguageDescriptor> dc = getDescriptorCache(); List<ILanguageDescriptor> list; String id; for (ILanguageDescriptor des : dc.values()) { IContentType types[] = des.getContentTypes(); for (IContentType type : types) { id = type.getId(); list = map.get(id); if (list == null) { list = new ArrayList<ILanguageDescriptor>(); map.put(id, list); } list.add(des); } } return map; } public ILanguage getLanguage(String id) { ILanguage language = fLanguageCache.get(id); if (language != null) return language; IConfigurationElement[] configs= Platform.getExtensionRegistry().getConfigurationElementsFor(LANGUAGE_EXTENSION_POINT_ID); for (int j = 0; j < configs.length; ++j) { final IConfigurationElement languageElem = configs[j]; if (ELEMENT_LANGUAGE.equals(languageElem.getName())) { String langId = getLanguageID(languageElem); if (langId.equals(id)) { final ILanguage[] result= new ILanguage[]{null}; SafeRunner.run(new ISafeRunnable() { public void handleException(Throwable exception) { CCorePlugin.log(exception); } public void run() throws Exception { result[0]= (ILanguage)languageElem.createExecutableExtension(ATTRIBUTE_CLASS); } }); if (result[0] != null) { fLanguageCache.put(id, result[0]); return result[0]; } } } } return null; } private String getLanguageID(final IConfigurationElement languageElem) { return languageElem.getNamespaceIdentifier() + NAMESPACE_SEPARATOR + languageElem.getAttribute(ATTRIBUTE_ID); } public ILanguage getLanguage(IContentType contentType) { String contentTypeID= contentType.getId(); return getLanguageForContentTypeID(contentTypeID); } public ILanguage getLanguageForContentTypeID(String contentTypeID) { cacheAllLanguages(); ILanguage language = fContentTypeToLanguageCache.get(contentTypeID); if (language != null || fContentTypeToLanguageCache.containsKey(contentTypeID)) return language; IConfigurationElement[] configs= Platform.getExtensionRegistry().getConfigurationElementsFor(LANGUAGE_EXTENSION_POINT_ID); for (int j = 0; j < configs.length; ++j) { final IConfigurationElement languageElem = configs[j]; if (ELEMENT_LANGUAGE.equals(languageElem.getName())) { IConfigurationElement[] assocContentTypes = languageElem.getChildren(ELEMENT_CONTENT_TYPE); for (int k = 0; k < assocContentTypes.length; ++k) { if (contentTypeID.equals(assocContentTypes[k].getAttribute(ATTRIBUTE_ID))) { String id= getLanguageID(languageElem); ILanguage lang= getLanguage(id); fContentTypeToLanguageCache.put(contentTypeID, lang); return lang; } } } } fContentTypeToLanguageCache.put(contentTypeID, null); return null; } /** * @deprecated use getRegisteredContentTypes() instead. */ @Deprecated public ArrayList<String> getAllContentTypes() { ArrayList<String> allTypes = new ArrayList<String>(); allTypes.add(CCorePlugin.CONTENT_TYPE_ASMSOURCE); allTypes.add(CCorePlugin.CONTENT_TYPE_CHEADER); allTypes.add(CCorePlugin.CONTENT_TYPE_CSOURCE); allTypes.add(CCorePlugin.CONTENT_TYPE_CXXHEADER); allTypes.add(CCorePlugin.CONTENT_TYPE_CXXSOURCE); IContentTypeManager manager = Platform.getContentTypeManager(); IConfigurationElement[] configs= Platform.getExtensionRegistry().getConfigurationElementsFor(LANGUAGE_EXTENSION_POINT_ID); for (int j = 0; j < configs.length; ++j) { final IConfigurationElement languageElem = configs[j]; if (ELEMENT_LANGUAGE.equals(languageElem.getName())) { IConfigurationElement[] contentTypes = languageElem.getChildren(ELEMENT_CONTENT_TYPE); for (int k = 0; k < contentTypes.length; ++k) { IContentType langContType = manager.getContentType(contentTypes[k].getAttribute(ATTRIBUTE_ID)); allTypes.add(langContType.getId()); } } } return allTypes; } /** * Returns all content types that are registered with CDT. * @since 3.1.1 */ public String[] getRegisteredContentTypeIds() { Set<String> contentTypes= collectContentTypeIds(); return contentTypes.toArray(new String[contentTypes.size()]); } private Set<String> collectContentTypeIds() { HashSet<String> allTypes = new HashSet<String>(); allTypes.add(CCorePlugin.CONTENT_TYPE_ASMSOURCE); allTypes.add(CCorePlugin.CONTENT_TYPE_CHEADER); allTypes.add(CCorePlugin.CONTENT_TYPE_CSOURCE); allTypes.add(CCorePlugin.CONTENT_TYPE_CXXHEADER); allTypes.add(CCorePlugin.CONTENT_TYPE_CXXSOURCE); IContentTypeManager manager = Platform.getContentTypeManager(); IConfigurationElement[] configs= Platform.getExtensionRegistry().getConfigurationElementsFor(LANGUAGE_EXTENSION_POINT_ID); for (int j = 0; j < configs.length; ++j) { final IConfigurationElement languageElem = configs[j]; if (ELEMENT_LANGUAGE.equals(languageElem.getName())) { IConfigurationElement[] contentTypes = languageElem.getChildren(ELEMENT_CONTENT_TYPE); for (int k = 0; k < contentTypes.length; ++k) { IContentType langContType = manager.getContentType(contentTypes[k].getAttribute(ATTRIBUTE_ID)); allTypes.add(langContType.getId()); } } } return allTypes; } public boolean isContributedContentType(String contentTypeId) { return contentTypeId != null && getLanguageForContentTypeID(contentTypeId) != null; } /** * @deprecated use {@link #getContributedModelBuilderFor(ITranslationUnit)}, instead. * @noreference This method is not intended to be referenced by clients. */ @Deprecated public IContributedModelBuilder getContributedModelBuilderFor(TranslationUnit tu) { try { ILanguage lang = tu.getLanguage(); return lang == null ? null : lang.createModelBuilder(tu); } catch (CoreException e) { return null; } } /** * @since 5.1 */ public IContributedModelBuilder getContributedModelBuilderFor(ITranslationUnit tu) { try { ILanguage lang = tu.getLanguage(); return lang == null ? null : lang.createModelBuilder(tu); } catch (CoreException e) { return null; } } /** * Returns mappings between IDs and IPDOMLinkageFactory. The IDs are defined in {@link ILinkage}. * @return a map. * @since 4.0 */ public Map<String, IPDOMLinkageFactory> getPDOMLinkageFactoryMappings() { if (!fPDOMLinkageFactoryCache.isEmpty()) return Collections.unmodifiableMap(fPDOMLinkageFactoryCache); fPDOMLinkageFactoryCache.clear(); final IPDOMLinkageFactory[] result = new IPDOMLinkageFactory[] {null}; // read configuration IConfigurationElement[] configs= Platform.getExtensionRegistry().getConfigurationElementsFor(LANGUAGE_EXTENSION_POINT_ID); for (final IConfigurationElement element : configs) { if (ELEMENT_PDOM_LINKAGE_FACTORY.equals(element.getName())) { SafeRunner.run(new ISafeRunnable() { public void handleException(Throwable exception) { CCorePlugin.log(exception); } public void run() throws Exception { result[0] = (IPDOMLinkageFactory) element.createExecutableExtension(ATTRIBUTE_CLASS); }} ); fPDOMLinkageFactoryCache.put(element.getAttribute(ATTRIBUTE_ID), result[0]); } } return Collections.unmodifiableMap(fPDOMLinkageFactoryCache); } /** * Returns all of the languages registered with the <code>Platform</code>. * @return all of the languages registered with the <code>Platform</code>. */ public ILanguage[] getRegisteredLanguages() { cacheAllLanguages(); ILanguage[] languages = new ILanguage[fLanguageCache.size()]; Iterator<ILanguage> values = fLanguageCache.values().iterator(); for (int i = 0; values.hasNext(); i++) { languages[i] = values.next(); } return languages; } private void cacheAllLanguages() { if (fIsFullyCached) { return; } IConfigurationElement[] configs= Platform.getExtensionRegistry().getConfigurationElementsFor(LANGUAGE_EXTENSION_POINT_ID); for (int j = 0; j < configs.length; ++j) { final IConfigurationElement languageElem = configs[j]; if (ELEMENT_LANGUAGE.equals(languageElem.getName())) { String langId = getLanguageID(languageElem); final ILanguage[] result= new ILanguage[] { null }; SafeRunner.run(new ISafeRunnable() { public void handleException(Throwable exception) { CCorePlugin.log(exception); } public void run() throws Exception { result[0]= (ILanguage) languageElem.createExecutableExtension(ATTRIBUTE_CLASS); } }); if (result[0] != null) { fLanguageCache.put(langId, result[0]); } } } fIsFullyCached = true; } /** * Returns the language configuration for the workspace. * @return the language configuration for the workspace * @throws CoreException * @since 4.0 */ public WorkspaceLanguageConfiguration getWorkspaceLanguageConfiguration() throws CoreException { synchronized (this) { if (fWorkspaceMappings != null) { return fWorkspaceMappings; } LanguageMappingStore store = new LanguageMappingStore(); fWorkspaceMappings = store.decodeWorkspaceMappings(); return fWorkspaceMappings; } } /** * Saves the workspace language configuration to persistent storage and notifies * all <code>ILanguageMappingChangeListeners</code> of changes. * @param affectedContentTypes * @throws CoreException * @since 4.0 */ public void storeWorkspaceLanguageConfiguration(IContentType[] affectedContentTypes) throws CoreException { 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(ILanguageMappingChangeEvent.TYPE_WORKSPACE); event.setAffectedContentTypes(affectedContentTypes); notifyLanguageChangeListeners(event); } /** * Returns the language configuration for the given project. * @param project * @return the language configuration for the given project * @throws CoreException * @since 4.0 */ public ProjectLanguageConfiguration getLanguageConfiguration(IProject project) throws CoreException { synchronized (this) { ProjectLanguageConfiguration mappings = fLanguageConfigurationCache.get(project); if (mappings != null) { return mappings; } LanguageMappingStore store = new LanguageMappingStore(); mappings = store.decodeMappings(project); fLanguageConfigurationCache.put(project, mappings); return mappings; } } /** * Saves the language configuration for the given project to persistent * storage and notifies all <code>ILanguageMappingChangeListeners</code> * of changes. * @param project * @param affectedContentTypes * @throws CoreException * @since 4.0 */ public void storeLanguageMappingConfiguration(IProject project, IContentType[] affectedContentTypes) throws CoreException { synchronized (this) { ProjectLanguageConfiguration mappings = fLanguageConfigurationCache.get(project); LanguageMappingStore store = new LanguageMappingStore(); store.storeMappings(project, mappings); } // Notify listeners that the language mappings have changed. LanguageMappingChangeEvent event = new LanguageMappingChangeEvent(); event.setType(ILanguageMappingChangeEvent.TYPE_PROJECT); event.setProject(project); event.setAffectedContentTypes(affectedContentTypes); notifyLanguageChangeListeners(event); } /** * Returns an ILanguage representing the language to be used for the given file. * @since 4.0 * @return an ILanguage representing the language to be used for the given file * @param fullPathToFile the full path to the file for which the language is requested * @param project the IProject that this file is in the context of. This field cannot be null. * @param configuration the active build configuration, or <code>null</code> if build configurations * are not relevant to determining the language. * @throws CoreException */ public ILanguage getLanguageForFile(String fullPathToFile, IProject project, ICConfigurationDescription configuration) throws CoreException { if (project == null) throw new IllegalArgumentException("project must not be null in call to LanguageManager.getLanguageForFile(String, IProject)"); //$NON-NLS-1$ IContentType contentType = CContentTypes.getContentType(project, fullPathToFile); if (contentType == null) { return null; } String contentTypeID = contentType.getId(); return LanguageMappingResolver.computeLanguage(project, fullPathToFile, configuration, contentTypeID, false)[0].language; } /** * Returns an ILanguage representing the language to be used for the given file. * @return an ILanguage representing the language to be used for the given file * @param pathToFile the path to the file for which the language is requested. * The path can be either workspace or project relative. * @param project the project that this file should be parsed in context of. This field is optional and may * be set to null. If the project is null then this method tries to determine the project context via workspace APIs. * @param configuration the active build configuration, or <code>null</code> if build configurations * are not relevant to determining the language. * @throws CoreException * @since 4.0 */ public ILanguage getLanguageForFile(IPath pathToFile, IProject project, ICConfigurationDescription configuration) throws CoreException { return getLanguageForFile(pathToFile, project, configuration, null); } /** * Returns an ILanguage representing the language to be used for the given file. * @return an ILanguage representing the language to be used for the given file * @param pathToFile the path to the file for which the language is requested. * The path can be either workspace or project relative. * @param project the project that this file should be parsed in context of. This field is optional and may * be set to null. If the project is null then this method tries to determine the project context via workspace APIs. * @param configuration the active build configuration, or <code>null</code> if build configurations * are not relevant to determining the language. * @param contentTypeID id of the content type, may be <code>null</code>. * @throws CoreException * @since 4.0 */ public ILanguage getLanguageForFile(IPath pathToFile, IProject project, ICConfigurationDescription configuration, String contentTypeID) throws CoreException { if (project == null) { IResource resource = ResourcesPlugin.getWorkspace().getRoot().getFileForLocation(pathToFile); if (resource == null) { return null; } project= resource.getProject(); } if (contentTypeID==null) { IContentType ct= CContentTypes.getContentType(project, pathToFile.toString()); if (ct == null) { return null; } contentTypeID= ct.getId(); } return LanguageMappingResolver.computeLanguage(project, pathToFile.toPortableString(), configuration, contentTypeID, false)[0].language; } /** * Returns an ILanguage representing the language to be used for the given file. * @return an ILanguage representing the language to be used for the given file * @param file the file for which the language is requested * @param configuration the active build configuration, or <code>null</code> if build configurations * are not relevant to determining the language. * @throws CoreException * @since 4.0 */ public ILanguage getLanguageForFile(IFile file, ICConfigurationDescription configuration) throws CoreException { return getLanguageForFile(file, configuration, null); } /** * Returns an ILanguage representing the language to be used for the given file. * @return an ILanguage representing the language to be used for the given file * @param file the file for which the language is requested * @param configuration the active build configuration, or <code>null</code> if build configurations * are not relevant to determining the language. * @param contentTypeId id of the content type, may be <code>null</code>. * @throws CoreException * @since 4.0 */ public ILanguage getLanguageForFile(IFile file, ICConfigurationDescription configuration, String contentTypeId) throws CoreException { IProject project = file.getProject(); if (contentTypeId == null) { IPath location = file.getLocation(); String filename; if (location != null) { filename = location.toString(); } else { filename = file.getName(); } IContentType contentType= CContentTypes.getContentType(project, filename); if (contentType == null) { return null; } contentTypeId= contentType.getId(); } return LanguageMappingResolver.computeLanguage(project, file.getProjectRelativePath().toPortableString(), configuration, contentTypeId, false)[0].language; } /** * Adds a listener that will be notified of changes in language mappings. * * @param listener the ILanguageMappingChangeListener to add */ public void registerLanguageChangeListener(ILanguageMappingChangeListener listener) { fLanguageChangeListeners.add(listener); } /** * Removes a language mapping change listener. * * @param listener the ILanguageMappingChangeListener to remove. */ public void unregisterLanguageChangeListener(ILanguageMappingChangeListener listener) { fLanguageChangeListeners.remove(listener); } /** * Notifies all language mappings change listeners of a change in the mappings. * * @param event the ILanguageMappingsChange event to be broadcast. */ public void notifyLanguageChangeListeners(ILanguageMappingChangeEvent event) { Object[] listeners = fLanguageChangeListeners.getListeners(); for (Object obj : listeners) { ILanguageMappingChangeListener listener = (ILanguageMappingChangeListener) obj; listener.handleLanguageMappingChangeEvent(event); } } /** * Saves the language configuration for the given file to persistent * storage and notifies all <code>ILanguageMappingChangeListeners</code> * of changes. * @param file * @throws CoreException * @since 4.0 */ public void storeLanguageMappingConfiguration(IFile file) throws CoreException { IProject project = file.getProject(); synchronized (this) { ProjectLanguageConfiguration mappings = fLanguageConfigurationCache.get(project); LanguageMappingStore store = new LanguageMappingStore(); store.storeMappings(project, mappings); } // Notify listeners that the language mappings have changed. LanguageMappingChangeEvent event = new LanguageMappingChangeEvent(); event.setType(ILanguageMappingChangeEvent.TYPE_FILE); event.setProject(project); event.setFile(file); notifyLanguageChangeListeners(event); } /** * Returns language binding to a particular content type for given project. * This method will check project settings, workspace settings and default * bindings (in that order) * * @param contentType content type of the file * @param project C/C++ workspace project * @return CDT language object * @since 5.4 */ public ILanguage getLanguage(IContentType contentType, IProject project) { return getLanguage(contentType, project, null); } /** * Returns language binding to a particular content type for given project. * This method will check project settings, workspace settings and default * bindings (in that order) * * @param contentType content type of the file * @param project C/C++ workspace project * @param configurationDescription build configuration or <code>null</code> * @return CDT language object * @since 5.4 */ public ILanguage getLanguage(IContentType contentType, IProject project, ICConfigurationDescription configurationDescription) { try { final ProjectLanguageConfiguration projectConfig = getLanguageConfiguration(project); final String contentTypeId = contentType.getId(); String langId = projectConfig.getLanguageForContentType(configurationDescription, contentTypeId); if (langId == null) { WorkspaceLanguageConfiguration wsConfig = getWorkspaceLanguageConfiguration(); langId = wsConfig.getLanguageForContentType(contentTypeId); } return langId != null ? getLanguage(langId) : getLanguage(contentType); } catch (CoreException e) { // Fall through to default language mapping } return getLanguage(contentType); } }