/******************************************************************************* * Copyright (c) 2007, 2013 David Green 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: * David Green - initial API and implementation *******************************************************************************/ package org.eclipse.mylyn.internal.wikitext.ui.registry; import java.text.MessageFormat; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IConfigurationElement; import org.eclipse.core.runtime.IExtensionPoint; import org.eclipse.core.runtime.ILog; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; import org.eclipse.mylyn.internal.wikitext.ui.WikiTextUiPlugin; import org.eclipse.mylyn.wikitext.parser.markup.MarkupLanguage; import org.eclipse.mylyn.wikitext.validation.MarkupValidator; import org.eclipse.mylyn.wikitext.validation.ValidationRule; import org.eclipse.mylyn.wikitext.validation.ValidationRules; import com.google.common.collect.Sets; public class WikiTextExtensionPointReader { private static final String EXTENSION_POINT_NAMESPACE = "org.eclipse.mylyn.wikitext.ui"; //$NON-NLS-1$ private static final String EXTENSION_MARKUP_LANGUAGE = "markupLanguage"; //$NON-NLS-1$ private static final String EXTENSION_VALIDATION_RULES = "markupValidationRule"; //$NON-NLS-1$ private static Object instanceLock = new Object(); private static WikiTextExtensionPointReader instance; private SortedMap<String, Class<? extends MarkupLanguage>> languageByName; private Map<String, Class<? extends MarkupLanguage>> languageByFileExtension; private Map<Class<? extends MarkupLanguage>, String> languageNameByLanguage; private Map<String, String> languageExtensionByLanguage; private Map<String, ValidationRules> validationRulesByLanguageName; public static WikiTextExtensionPointReader instance() { synchronized (instanceLock) { if (instance == null) { instance = new WikiTextExtensionPointReader(); } return instance; } } private WikiTextExtensionPointReader() { // prevent instantiation } /** * Get a markup language by name. * * @param name * the name of the markup language to retrieve * @return the markup language or null if there is no markup language known by the given name * @see #getMarkupLanguageNames() */ public MarkupLanguage getMarkupLanguage(String name) { if (languageByName == null) { initializeMarkupLanguages(); } Class<? extends MarkupLanguage> languageClass = languageByName.get(name); if (languageClass == null) { // if not found by name, attempt to lookup by class name. for (Entry<String, Class<? extends MarkupLanguage>> entry : languageByName.entrySet()) { Class<? extends MarkupLanguage> clazz = entry.getValue(); if (clazz.getName().equals(name)) { languageClass = clazz; name = entry.getKey(); break; } } } if (languageClass != null) { return instantiateMarkupLanguage(name, languageClass); } return null; } private MarkupLanguage instantiateMarkupLanguage(String name, Class<? extends MarkupLanguage> languageClass) { try { MarkupLanguage language = languageClass.newInstance(); language.setName(name); language.setExtendsLanguage(languageExtensionByLanguage.get(name)); configureFileExtensions(language); return language; } catch (Exception e) { log(IStatus.ERROR, MessageFormat.format(Messages.getString("WikiTextExtensionPointReader.2"), name, //$NON-NLS-1$ languageClass.getName(), e.getMessage()), e); } return null; } private void configureFileExtensions(MarkupLanguage language) { Set<String> fileExtensions = Sets.newHashSet(); for (Entry<String, Class<? extends MarkupLanguage>> entry : languageByFileExtension.entrySet()) { if (entry.getValue() == language.getClass()) { fileExtensions.add(entry.getKey()); } } if (!fileExtensions.isEmpty()) { language.setFileExtensions(fileExtensions); } } /** * Get a markup language name for a file. A markup language is selected based on the registered languages and their * expected file extensions. * * @param name * the name of the file for which a markup language is desired * @return the markup language name, or null if no markup language is registered for the specified file name * @see #getMarkupLanguageForFilename(String) */ public String getMarkupLanguageNameForFilename(String name) { if (languageByFileExtension == null) { initializeMarkupLanguages(); } int lastIndexOfDot = name.lastIndexOf('.'); String extension = lastIndexOfDot == -1 ? name : name.substring(lastIndexOfDot + 1); extension = extension.toLowerCase(); Class<? extends MarkupLanguage> languageClass = languageByFileExtension.get(extension); if (languageClass != null) { return languageNameByLanguage.get(languageClass); } return null; } /** * Get the file extensions that are registered for markup languages. File extensions are specified without the * leading dot. */ public Set<String> getMarkupFileExtensions() { if (languageByFileExtension == null) { initializeMarkupLanguages(); } return Collections.unmodifiableSet(languageByFileExtension.keySet()); } /** * Get a markup language for a file. A markup language is selected based on the registered languages and their * expected file extensions. * * @param name * the name of the file for which a markup language is desired * @return the markup language, or null if no markup language is registered for the specified file name * @see #getMarkupLanguageForFilename(String) */ public MarkupLanguage getMarkupLanguageForFilename(String name) { if (languageByFileExtension == null) { initializeMarkupLanguages(); } int lastIndexOfDot = name.lastIndexOf('.'); String extension = lastIndexOfDot == -1 ? name : name.substring(lastIndexOfDot + 1); Class<? extends MarkupLanguage> languageClass = languageByFileExtension.get(extension); if (languageClass != null) { String languageName = null; for (Map.Entry<String, Class<? extends MarkupLanguage>> ent : languageByName.entrySet()) { if (ent.getValue() == languageClass) { languageName = ent.getKey(); break; } } return instantiateMarkupLanguage(languageName, languageClass); } return null; } /** * Get the names of all known markup languages * * @see #getMarkupLanguage(String) */ public Set<String> getMarkupLanguageNames() { if (languageByName == null) { initializeMarkupLanguages(); } return languageByName.keySet(); } /** * Get a markup validator by language name. * * @param name * the name of the markup language for which a validator is desired * @return the markup validator * @see #getMarkupLanguageNames() */ public MarkupValidator getMarkupValidator(String name) { MarkupValidator markupValidator = new MarkupValidator(); if (validationRulesByLanguageName == null) { initializeValidationRules(); } ValidationRules rules = validationRulesByLanguageName.get(name); if (rules != null) { markupValidator.getRules().addAll(rules.getRules()); } return markupValidator; } private void initializeValidationRules() { initializeMarkupLanguages(); synchronized (this) { if (validationRulesByLanguageName == null) { Map<String, ValidationRules> validationRulesByLanguageName = new HashMap<>(); IExtensionPoint extensionPoint = Platform.getExtensionRegistry() .getExtensionPoint(getExtensionPointNamespace(), EXTENSION_VALIDATION_RULES); if (extensionPoint != null) { IConfigurationElement[] configurationElements = extensionPoint.getConfigurationElements(); for (IConfigurationElement element : configurationElements) { try { String markupLanguage = element.getAttribute("markupLanguage"); //$NON-NLS-1$ if (markupLanguage == null || markupLanguage.length() == 0) { throw new Exception(Messages.getString("WikiTextExtensionPointReader.4")); //$NON-NLS-1$ } if (!languageByName.containsKey(markupLanguage)) { throw new Exception(MessageFormat .format(Messages.getString("WikiTextExtensionPointReader.5"), languageByName)); //$NON-NLS-1$ } Object extension; try { extension = element.createExecutableExtension("class"); //$NON-NLS-1$ } catch (CoreException e) { getLog().log(e.getStatus()); continue; } if (!(extension instanceof ValidationRule)) { throw new Exception(MessageFormat.format( Messages.getString("WikiTextExtensionPointReader.7"), extension.getClass() //$NON-NLS-1$ .getName())); } ValidationRules rules = validationRulesByLanguageName.get(markupLanguage); if (rules == null) { rules = new ValidationRules(); validationRulesByLanguageName.put(markupLanguage, rules); } rules.addValidationRule((ValidationRule) extension); } catch (Exception e) { log(IStatus.ERROR, MessageFormat.format(Messages.getString("WikiTextExtensionPointReader.8"), //$NON-NLS-1$ element.getDeclaringExtension().getContributor().getName(), EXTENSION_VALIDATION_RULES, e.getMessage()), e); } } } // now that we have the basic validation rules, check for language extensions and connect the hierarchy // first ensure that all language names have templates defined Set<String> languageNames = getMarkupLanguageNames(); for (String languageName : languageNames) { ValidationRules rules = validationRulesByLanguageName.get(languageName); if (rules == null) { rules = new ValidationRules(); validationRulesByLanguageName.put(languageName, rules); } } // next connect the hierarchy for (String languageName : languageNames) { MarkupLanguage markupLanguage = getMarkupLanguage(languageName); if (markupLanguage != null && markupLanguage.getExtendsLanguage() != null) { ValidationRules languageRules = validationRulesByLanguageName.get(languageName); ValidationRules parentLanguageRules = validationRulesByLanguageName .get(markupLanguage.getExtendsLanguage()); languageRules.setParent(parentLanguageRules); } } this.validationRulesByLanguageName = validationRulesByLanguageName; } } } private void initializeMarkupLanguages() { synchronized (this) { if (this.languageByName == null) { SortedMap<String, Class<? extends MarkupLanguage>> markupLanguageByName = new TreeMap<>(); Map<String, Class<? extends MarkupLanguage>> languageByFileExtension = new HashMap<>(); Map<String, String> languageExtensionByLanguage = new HashMap<>(); Map<Class<? extends MarkupLanguage>, String> languageNameByLanguage = new HashMap<>(); IExtensionPoint extensionPoint = Platform.getExtensionRegistry() .getExtensionPoint(getExtensionPointNamespace(), EXTENSION_MARKUP_LANGUAGE); if (extensionPoint != null) { IConfigurationElement[] configurationElements = extensionPoint.getConfigurationElements(); for (IConfigurationElement element : configurationElements) { String name = element.getAttribute("name"); //$NON-NLS-1$ if (name == null || name.length() == 0) { log(IStatus.ERROR, MessageFormat.format( EXTENSION_MARKUP_LANGUAGE + Messages.getString("WikiTextExtensionPointReader.10"), //$NON-NLS-1$ element.getDeclaringExtension().getContributor().getName())); continue; } String extendsLanguage = element.getAttribute("extends"); //$NON-NLS-1$ Object markupLanguage; try { markupLanguage = element.createExecutableExtension("class"); //$NON-NLS-1$ } catch (CoreException e) { getLog().log(e.getStatus()); continue; } if (!(markupLanguage instanceof MarkupLanguage)) { log(IStatus.ERROR, MessageFormat.format(Messages.getString("WikiTextExtensionPointReader.13"), //$NON-NLS-1$ markupLanguage.getClass().getName())); continue; } MarkupLanguage d = (MarkupLanguage) markupLanguage; { Class<? extends MarkupLanguage> previous = markupLanguageByName.put(name, d.getClass()); if (previous != null) { log(IStatus.ERROR, MessageFormat.format( EXTENSION_MARKUP_LANGUAGE + Messages.getString("WikiTextExtensionPointReader.14"), //$NON-NLS-1$ name, element.getDeclaringExtension().getContributor().getName(), name)); markupLanguageByName.put(name, previous); continue; } else { languageNameByLanguage.put(d.getClass(), name); } } if (extendsLanguage != null) { languageExtensionByLanguage.put(name, extendsLanguage); } String fileExtensions = element.getAttribute("fileExtensions"); //$NON-NLS-1$ if (fileExtensions != null) { String[] parts = fileExtensions.split("\\s*,\\s*"); //$NON-NLS-1$ for (String part : parts) { if (part.length() != 0) { part = part.toLowerCase(); Class<? extends MarkupLanguage> previous = languageByFileExtension.put(part, d.getClass()); if (previous != null) { log(IStatus.ERROR, MessageFormat.format( EXTENSION_MARKUP_LANGUAGE + Messages.getString("WikiTextExtensionPointReader.17"), //$NON-NLS-1$ part, element.getDeclaringExtension().getContributor().getName(), part)); languageByFileExtension.put(part, previous); continue; } } } } } } this.languageByFileExtension = languageByFileExtension; this.languageByName = markupLanguageByName; this.languageExtensionByLanguage = languageExtensionByLanguage; this.languageNameByLanguage = languageNameByLanguage; } } } private ILog getLog() { return WikiTextUiPlugin.getDefault().getLog(); } private void log(int severity, String message) { log(severity, message, null); } private void log(int severity, String message, Throwable t) { getLog().log(new Status(severity, WikiTextUiPlugin.getDefault().getPluginId(), message, t)); } private String getExtensionPointNamespace() { return EXTENSION_POINT_NAMESPACE; } }