]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-2497 Merge i18n plugin with core
authorSimon Brandhof <simon.brandhof@gmail.com>
Tue, 21 Jun 2011 16:06:11 +0000 (18:06 +0200)
committerSimon Brandhof <simon.brandhof@gmail.com>
Tue, 21 Jun 2011 16:13:15 +0000 (18:13 +0200)
41 files changed:
plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java
plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/i18n/I18nManager.java [new file with mode: 0644]
plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/i18n/InstalledPlugin.java [new file with mode: 0644]
plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/i18n/FrenchLanguagePack.java [new file with mode: 0644]
plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/i18n/I18nManagerTest.java [new file with mode: 0644]
plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/i18n/QuebecLanguagePack.java [new file with mode: 0644]
plugins/sonar-core-plugin/src/test/resources/FrenchPlugin.jar [new file with mode: 0644]
plugins/sonar-core-plugin/src/test/resources/FrenchPlugin/META-INF/MANIFEST.MF [new file with mode: 0644]
plugins/sonar-core-plugin/src/test/resources/FrenchPlugin/org/sonar/i18n/test_fr.properties [new file with mode: 0644]
plugins/sonar-core-plugin/src/test/resources/QuebecPlugin.jar [new file with mode: 0644]
plugins/sonar-core-plugin/src/test/resources/QuebecPlugin/META-INF/MANIFEST.MF [new file with mode: 0644]
plugins/sonar-core-plugin/src/test/resources/QuebecPlugin/org/sonar/i18n/test_fr_CA.properties [new file with mode: 0644]
plugins/sonar-core-plugin/src/test/resources/StandardPlugin.jar [new file with mode: 0644]
plugins/sonar-core-plugin/src/test/resources/StandardPlugin/META-INF/MANIFEST.MF [new file with mode: 0644]
plugins/sonar-core-plugin/src/test/resources/StandardPlugin/org/sonar/i18n/test.properties [new file with mode: 0644]
plugins/sonar-core-plugin/src/test/resources/logback-test.xml [new file with mode: 0644]
plugins/sonar-i18n-plugin/src/main/java/org/sonar/plugins/i18n/I18nManager.java [deleted file]
plugins/sonar-i18n-plugin/src/main/java/org/sonar/plugins/i18n/I18nPlugin.java [deleted file]
plugins/sonar-i18n-plugin/src/main/java/org/sonar/plugins/i18n/InstalledPlugin.java [deleted file]
plugins/sonar-i18n-plugin/src/test/java/org/sonar/plugins/i18n/I18nManagerTest.java [deleted file]
plugins/sonar-i18n-plugin/src/test/java/org/sonar/plugins/i18n/I18nPluginTest.java [deleted file]
plugins/sonar-i18n-plugin/src/test/java/org/sonar/plugins/i18n/utils/FrenchLanguagePack.java [deleted file]
plugins/sonar-i18n-plugin/src/test/java/org/sonar/plugins/i18n/utils/QuebecLanguagePack.java [deleted file]
plugins/sonar-i18n-plugin/src/test/resources/FrenchPlugin.jar [deleted file]
plugins/sonar-i18n-plugin/src/test/resources/FrenchPlugin/META-INF/MANIFEST.MF [deleted file]
plugins/sonar-i18n-plugin/src/test/resources/FrenchPlugin/org/sonar/i18n/test_fr.properties [deleted file]
plugins/sonar-i18n-plugin/src/test/resources/QuebecPlugin.jar [deleted file]
plugins/sonar-i18n-plugin/src/test/resources/QuebecPlugin/META-INF/MANIFEST.MF [deleted file]
plugins/sonar-i18n-plugin/src/test/resources/QuebecPlugin/org/sonar/i18n/test_fr_CA.properties [deleted file]
plugins/sonar-i18n-plugin/src/test/resources/StandardPlugin.jar [deleted file]
plugins/sonar-i18n-plugin/src/test/resources/StandardPlugin/META-INF/MANIFEST.MF [deleted file]
plugins/sonar-i18n-plugin/src/test/resources/StandardPlugin/org/sonar/i18n/test.properties [deleted file]
plugins/sonar-i18n-plugin/src/test/resources/logback-test.xml [deleted file]
pom.xml
sonar-application/pom.xml
sonar-application/src/main/assembly/conf/logback.xml
sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchPluginRepository.java
sonar-batch/src/test/java/org/sonar/batch/bootstrap/BatchPluginRepositoryTest.java
sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java
sonar-server/pom.xml
sonar-server/src/main/webapp/WEB-INF/app/controllers/i18n_controller.rb [new file with mode: 0644]

index f3b72b9e0c1925beaf5fbd99a05b65457025d270..87d1e4064420ea517dea6fc6f8bf5bac808b6f1f 100644 (file)
@@ -34,6 +34,7 @@ import org.sonar.plugins.core.charts.XradarChart;
 import org.sonar.plugins.core.colorizers.JavaColorizerFormat;
 import org.sonar.plugins.core.duplicationsviewer.DuplicationsViewerDefinition;
 import org.sonar.plugins.core.hotspots.Hotspots;
+import org.sonar.plugins.core.i18n.I18nManager;
 import org.sonar.plugins.core.metrics.UserManagedMetrics;
 import org.sonar.plugins.core.security.ApplyProjectRolesDecorator;
 import org.sonar.plugins.core.sensors.*;
@@ -227,6 +228,9 @@ public class CorePlugin extends SonarPlugin {
     extensions.add(NewCoverageFileAnalyzer.class);
     extensions.add(NewCoverageAggregator.class);
 
+    // i18n
+    extensions.add(I18nManager.class);
+
     return extensions;
   }
 }
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/i18n/I18nManager.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/i18n/I18nManager.java
new file mode 100644 (file)
index 0000000..8438b70
--- /dev/null
@@ -0,0 +1,203 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
+ */
+package org.sonar.plugins.core.i18n;
+
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import org.apache.commons.collections.EnumerationUtils;
+import org.apache.commons.io.IOUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.sonar.api.BatchExtension;
+import org.sonar.api.ServerExtension;
+import org.sonar.api.i18n.I18n;
+import org.sonar.api.i18n.LanguagePack;
+import org.sonar.api.platform.PluginRepository;
+import org.sonar.api.utils.Logs;
+import org.sonar.api.utils.SonarException;
+
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.text.MessageFormat;
+import java.util.*;
+
+public final class I18nManager implements I18n, ServerExtension, BatchExtension {
+
+  private static final Logger LOG = LoggerFactory.getLogger(I18nManager.class);
+  public static final String packagePathToSearchIn = "org/sonar/i18n";
+
+  private PluginRepository pluginRepository;
+  private LanguagePack[] languagePacks;
+  private Map<String, String> keys = Maps.newHashMap();
+  private Properties unknownKeys = new Properties();
+  private BundleClassLoader bundleClassLoader = new BundleClassLoader();
+
+  public I18nManager(PluginRepository pluginRepository, LanguagePack[] languagePacks) {
+    this.pluginRepository = pluginRepository;
+    this.languagePacks = languagePacks;
+  }
+
+  public I18nManager(PluginRepository pluginRepository) {
+    this(pluginRepository, new LanguagePack[0]);
+  }
+
+
+  public void start() {
+    doStart(InstalledPlugin.create(pluginRepository));
+  }
+
+  void doStart(List<InstalledPlugin> installedPlugins) {
+    Logs.INFO.info("Loading i18n bundles");
+    Set<URI> alreadyLoadedResources = Sets.newHashSet();
+    for (InstalledPlugin plugin : installedPlugins) {
+      searchAndStoreBundleNames(plugin.key, plugin.classloader, alreadyLoadedResources);
+    }
+    for (LanguagePack pack : languagePacks) {
+      addLanguagePack(pack);
+    }
+  }
+
+  private void addLanguagePack(LanguagePack languagePack) {
+    LOG.debug("Search for bundles in language pack : {}", languagePack);
+    for (String pluginKey : languagePack.getPluginKeys()) {
+      String bundleBaseName = buildBundleBaseName(pluginKey);
+      for (Locale locale : languagePack.getLocales()) {
+        String bundlePropertiesFile = new StringBuilder(bundleBaseName).append('_').append(locale.toString()).append(".properties").toString();
+        ClassLoader classloader = languagePack.getClass().getClassLoader();
+        LOG.info("Adding locale {} for bundleName : {} from classloader : {}", new Object[]{locale, bundleBaseName, classloader});
+        bundleClassLoader.addResource(bundlePropertiesFile, classloader);
+      }
+    }
+  }
+
+  private String buildBundleBaseName(String pluginKey) {
+    return packagePathToSearchIn + "/" + pluginKey;
+  }
+
+  private void searchAndStoreBundleNames(String pluginKey, ClassLoader classloader, Set<URI> alreadyLoadedResources) {
+    String bundleBaseName = buildBundleBaseName(pluginKey);
+    String bundleDefaultPropertiesFile = bundleBaseName + ".properties";
+    try {
+      LOG.debug("Search for ResourceBundle base file '" + bundleDefaultPropertiesFile + "' in the classloader : " + classloader);
+      List<URL> resources = EnumerationUtils.toList(classloader.getResources(bundleDefaultPropertiesFile));
+      if (resources.size() > 0) {
+        if (resources.size() > 1) {
+          LOG.warn("File '{}' found several times in the classloader : {}. Only the first one will be taken in account.",
+              bundleDefaultPropertiesFile, classloader);
+        }
+
+        URL propertiesUrl = resources.get(0);
+        if (!alreadyLoadedResources.contains(propertiesUrl.toURI())) {
+          LOG.debug("Found the ResourceBundle base file : {} from classloader : {}", propertiesUrl, classloader);
+          LOG.debug("Add bundleName : {} from classloader : {}", bundleBaseName, classloader);
+          bundleClassLoader.addResource(bundleDefaultPropertiesFile, classloader);
+          alreadyLoadedResources.add(propertiesUrl.toURI());
+
+          Properties bundleContent = new Properties();
+          InputStream input = null;
+          try {
+            input = propertiesUrl.openStream();
+            bundleContent.load(input);
+            Enumeration<String> keysToAdd = (Enumeration<String>) bundleContent.propertyNames();
+            while (keysToAdd.hasMoreElements()) {
+              String key = keysToAdd.nextElement();
+              if (keys.containsKey(key)) {
+                LOG.warn("DUPLICATE KEY : Key '{}' defined in bundle '{}' is already defined in bundle '{}'. It is ignored.", new Object[]{
+                    key, bundleBaseName, keys.get(key)});
+              } else {
+                keys.put(key, bundleBaseName);
+              }
+            }
+          } finally {
+            IOUtils.closeQuietly(input);
+          }
+        }
+      }
+    } catch (Exception e) {
+      LOG.error("Fail to load '" + bundleDefaultPropertiesFile + "' in classloader : " + classloader, e);
+      throw new SonarException("Fail to load '" + bundleDefaultPropertiesFile + "' in classloader : " + classloader, e);
+    }
+  }
+
+  public String message(final Locale locale, final String key, final String defaultText, final Object... objects) {
+    String result = defaultText;
+    try {
+      String bundleBaseName = keys.get(key);
+      if (bundleBaseName == null) {
+        LOG.warn("UNKNOWN KEY : Key '{}' not found in any bundle. Default value '{}' is returned.", key, defaultText);
+        unknownKeys.put(key, defaultText);
+      } else {
+        try {
+          ResourceBundle bundle = ResourceBundle.getBundle(bundleBaseName, locale, bundleClassLoader);
+
+          String value = bundle.getString(key);
+          if ("".equals(value)) {
+            LOG.warn("VOID KEY : Key '{}' (from bundle '{}') returns a void value. Default value '{}' is returned.", new Object[]{key,
+                bundleBaseName, defaultText});
+          } else {
+            result = value;
+          }
+        } catch (MissingResourceException e) {
+          LOG.warn("BUNDLE NOT LOADED : Failed loading bundle {} from classloader {}. Default value '{}' is returned.", new Object[]{
+              bundleBaseName, bundleClassLoader, defaultText});
+        }
+      }
+    } catch (Exception e) {
+      LOG.error("Exception when retrieving I18n string.", e);
+    }
+
+    if (objects.length > 0) {
+      LOG.debug("Translation : {}, {}, {}, {}", new String[]{locale.toString(), key, defaultText, Arrays.deepToString(objects)});
+      return MessageFormat.format(result, objects);
+    } else {
+      return result;
+    }
+  }
+
+  /**
+   * @return the unknownKeys
+   */
+  public Properties getUnknownKeys() {
+    return unknownKeys;
+  }
+
+
+  private static class BundleClassLoader extends URLClassLoader {
+    private Map<String, ClassLoader> resources = Maps.newHashMap();
+
+    public BundleClassLoader() {
+      super(new URL[]{}, null);
+    }
+
+    public void addResource(String resourceName, ClassLoader classloader) {
+      resources.put(resourceName, classloader);
+    }
+
+    @Override
+    public URL findResource(String name) {
+      if (resources.containsKey(name)) {
+        return resources.get(name).getResource(name);
+      }
+      return null;
+    }
+  }
+}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/i18n/InstalledPlugin.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/i18n/InstalledPlugin.java
new file mode 100644 (file)
index 0000000..abe4b3c
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
+ */
+package org.sonar.plugins.core.i18n;
+
+import com.google.common.collect.Lists;
+import org.sonar.api.Plugin;
+import org.sonar.api.platform.PluginMetadata;
+import org.sonar.api.platform.PluginRepository;
+
+import java.util.List;
+
+class InstalledPlugin {
+  String key;
+  ClassLoader classloader;
+
+  InstalledPlugin(String key, ClassLoader classloader) {
+    this.key = key;
+    this.classloader = classloader;
+  }
+
+  static List<InstalledPlugin> create(PluginRepository pluginRepository) {
+    List<InstalledPlugin> result = Lists.newArrayList();
+    for (PluginMetadata metadata : pluginRepository.getMetadata()) {
+      Plugin entryPoint = pluginRepository.getPlugin(metadata.getKey());
+      result.add(new InstalledPlugin(metadata.getKey(), entryPoint.getClass().getClassLoader()));
+    }
+    return result;
+  }
+}
diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/i18n/FrenchLanguagePack.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/i18n/FrenchLanguagePack.java
new file mode 100644 (file)
index 0000000..e781f5a
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
+ */
+package org.sonar.plugins.core.i18n;
+
+import org.sonar.api.i18n.LanguagePack;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Locale;
+
+public class FrenchLanguagePack extends LanguagePack {
+
+  @Override
+  public List<String> getPluginKeys() {
+    return Arrays.asList("test");
+  }
+
+  @Override
+  public List<Locale> getLocales() {
+    return Arrays.asList(Locale.FRENCH);
+  }
+}
\ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/i18n/I18nManagerTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/i18n/I18nManagerTest.java
new file mode 100644 (file)
index 0000000..380f65e
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
+ */
+package org.sonar.plugins.core.i18n;
+
+import com.google.common.collect.Lists;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.sonar.api.i18n.LanguagePack;
+import org.sonar.api.platform.PluginRepository;
+
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Locale;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+
+public class I18nManagerTest {
+
+  public static String TEST_PLUGIN_CLASS_NAME = "org.sonar.plugins.core.i18n.StandardPlugin";
+  public static String FRENCH_PACK_CLASS_NAME = "org.sonar.plugins.core.i18n.FrenchLanguagePack";
+  public static String QUEBEC_PACK_CLASS_NAME = "org.sonar.plugins.core.i18n.QuebecLanguagePack";
+
+  private static URL classSource = I18nManagerTest.class.getProtectionDomain().getCodeSource().getLocation();
+  private I18nManager manager;
+
+  @Before
+  public void createManager() throws Exception {
+    List<InstalledPlugin> plugins = Lists.newArrayList(
+        new InstalledPlugin("test", new TestClassLoader(getClass().getClassLoader().getResource("StandardPlugin.jar"))),
+        new InstalledPlugin("fake1", getClass().getClassLoader()),
+        new InstalledPlugin("fake2", getClass().getClassLoader())
+    );
+
+    TestClassLoader frenchPackClassLoader = new TestClassLoader(getClass().getClassLoader().getResource("FrenchPlugin.jar"));
+    LanguagePack frenchPack = (LanguagePack) frenchPackClassLoader.loadClass(FRENCH_PACK_CLASS_NAME).newInstance();
+
+    TestClassLoader quebecPackClassLoader = new TestClassLoader(getClass().getClassLoader().getResource("QuebecPlugin.jar"));
+    LanguagePack quebecPack = (LanguagePack) quebecPackClassLoader.loadClass(QUEBEC_PACK_CLASS_NAME).newInstance();
+
+    manager = new I18nManager(mock(PluginRepository.class), new LanguagePack[]{frenchPack, quebecPack});
+    manager.doStart(plugins);
+  }
+
+  @Test
+  public void shouldTranslateWithoutRegionalVariant() {
+    List<String> sentence = Arrays.asList("it", "is", "cold");
+    String result = "";
+    for (String token : sentence) {
+      result += manager.message(Locale.FRENCH, token, token) + " ";
+    }
+    assertEquals("Il fait froid ", result);
+  }
+
+  @Test
+  public void shouldTranslateWithRegionalVariant() {
+    // it & is are taken from the french language pack
+    // and cold is taken from the quebec language pack
+    List<String> sentence = Arrays.asList("it", "is", "cold");
+    String result = "";
+    for (String token : sentence) {
+      result += manager.message(Locale.CANADA_FRENCH, token, token) + " ";
+    }
+    assertEquals("Il fait frette ", result);
+  }
+
+  @Test
+  public void shouldTranslateReturnsDefaultBundleValue() {
+    String result = manager.message(Locale.FRENCH, "only.english", "Default");
+    assertEquals("Ketchup", result);
+  }
+
+  @Test
+  public void shouldTranslateUnknownValue() {
+    String result = manager.message(Locale.FRENCH, "unknown", "Default value for Unknown");
+    assertEquals("Default value for Unknown", result);
+    Assert.assertEquals(1, manager.getUnknownKeys().size());
+    Assert.assertEquals("Default value for Unknown", manager.getUnknownKeys().getProperty("unknown"));
+  }
+
+  public static class TestClassLoader extends URLClassLoader {
+    public TestClassLoader(URL url) {
+      super(new URL[]{url, classSource}, Thread.currentThread().getContextClassLoader());
+    }
+
+    protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
+      Class c = findLoadedClass(name);
+      if (c == null) {
+        if (name.equals(TEST_PLUGIN_CLASS_NAME) || name.equals(QUEBEC_PACK_CLASS_NAME) || name.equals(FRENCH_PACK_CLASS_NAME)) {
+          c = findClass(name);
+        } else {
+          return super.loadClass(name, resolve);
+        }
+      }
+      if (resolve) {
+        resolveClass(c);
+      }
+      return c;
+    }
+  }
+
+}
diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/i18n/QuebecLanguagePack.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/i18n/QuebecLanguagePack.java
new file mode 100644 (file)
index 0000000..c8ed96a
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
+ */
+package org.sonar.plugins.core.i18n;
+
+import org.sonar.api.i18n.LanguagePack;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Locale;
+
+public class QuebecLanguagePack extends LanguagePack {
+
+  @Override
+  public List<String> getPluginKeys() {
+    return Arrays.asList("test");
+  }
+
+  @Override
+  public List<Locale> getLocales() {
+    return Arrays.asList(Locale.CANADA_FRENCH);
+  }
+}
\ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/test/resources/FrenchPlugin.jar b/plugins/sonar-core-plugin/src/test/resources/FrenchPlugin.jar
new file mode 100644 (file)
index 0000000..ab61bd8
Binary files /dev/null and b/plugins/sonar-core-plugin/src/test/resources/FrenchPlugin.jar differ
diff --git a/plugins/sonar-core-plugin/src/test/resources/FrenchPlugin/META-INF/MANIFEST.MF b/plugins/sonar-core-plugin/src/test/resources/FrenchPlugin/META-INF/MANIFEST.MF
new file mode 100644 (file)
index 0000000..a45029d
--- /dev/null
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0\r
+Created-By: 1.6.0_17 (Apple Inc.)\r
+\r
diff --git a/plugins/sonar-core-plugin/src/test/resources/FrenchPlugin/org/sonar/i18n/test_fr.properties b/plugins/sonar-core-plugin/src/test/resources/FrenchPlugin/org/sonar/i18n/test_fr.properties
new file mode 100644 (file)
index 0000000..cc8efa7
--- /dev/null
@@ -0,0 +1,4 @@
+it=Il
+is=fait
+cold=froid
+
diff --git a/plugins/sonar-core-plugin/src/test/resources/QuebecPlugin.jar b/plugins/sonar-core-plugin/src/test/resources/QuebecPlugin.jar
new file mode 100644 (file)
index 0000000..101df34
Binary files /dev/null and b/plugins/sonar-core-plugin/src/test/resources/QuebecPlugin.jar differ
diff --git a/plugins/sonar-core-plugin/src/test/resources/QuebecPlugin/META-INF/MANIFEST.MF b/plugins/sonar-core-plugin/src/test/resources/QuebecPlugin/META-INF/MANIFEST.MF
new file mode 100644 (file)
index 0000000..a45029d
--- /dev/null
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0\r
+Created-By: 1.6.0_17 (Apple Inc.)\r
+\r
diff --git a/plugins/sonar-core-plugin/src/test/resources/QuebecPlugin/org/sonar/i18n/test_fr_CA.properties b/plugins/sonar-core-plugin/src/test/resources/QuebecPlugin/org/sonar/i18n/test_fr_CA.properties
new file mode 100644 (file)
index 0000000..38b5b84
--- /dev/null
@@ -0,0 +1,2 @@
+cold=frette
+
diff --git a/plugins/sonar-core-plugin/src/test/resources/StandardPlugin.jar b/plugins/sonar-core-plugin/src/test/resources/StandardPlugin.jar
new file mode 100644 (file)
index 0000000..73deac7
Binary files /dev/null and b/plugins/sonar-core-plugin/src/test/resources/StandardPlugin.jar differ
diff --git a/plugins/sonar-core-plugin/src/test/resources/StandardPlugin/META-INF/MANIFEST.MF b/plugins/sonar-core-plugin/src/test/resources/StandardPlugin/META-INF/MANIFEST.MF
new file mode 100644 (file)
index 0000000..a45029d
--- /dev/null
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0\r
+Created-By: 1.6.0_17 (Apple Inc.)\r
+\r
diff --git a/plugins/sonar-core-plugin/src/test/resources/StandardPlugin/org/sonar/i18n/test.properties b/plugins/sonar-core-plugin/src/test/resources/StandardPlugin/org/sonar/i18n/test.properties
new file mode 100644 (file)
index 0000000..88aabfe
--- /dev/null
@@ -0,0 +1,4 @@
+it=It
+is=is
+cold=cold
+only.english=Ketchup
diff --git a/plugins/sonar-core-plugin/src/test/resources/logback-test.xml b/plugins/sonar-core-plugin/src/test/resources/logback-test.xml
new file mode 100644 (file)
index 0000000..cdc8866
--- /dev/null
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<configuration>
+  <appender name="STDOUT"
+            class="ch.qos.logback.core.ConsoleAppender">
+    <layout class="ch.qos.logback.classic.PatternLayout">
+      <pattern>
+        %d{HH:mm:ss.SSS} %-5level %logger{36} - %msg%n
+      </pattern>
+    </layout>
+  </appender>
+
+  <root>
+    <level value="INFO"/>
+    <appender-ref ref="STDOUT"/>
+  </root>
+</configuration>
\ No newline at end of file
diff --git a/plugins/sonar-i18n-plugin/src/main/java/org/sonar/plugins/i18n/I18nManager.java b/plugins/sonar-i18n-plugin/src/main/java/org/sonar/plugins/i18n/I18nManager.java
deleted file mode 100644 (file)
index 6846f88..0000000
+++ /dev/null
@@ -1,203 +0,0 @@
-/*
- * Sonar, open source software quality management tool.
- * Copyright (C) 2008-2011 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * Sonar is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * Sonar is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with Sonar; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
- */
-package org.sonar.plugins.i18n;
-
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
-import org.apache.commons.collections.EnumerationUtils;
-import org.apache.commons.io.IOUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.sonar.api.BatchExtension;
-import org.sonar.api.ServerExtension;
-import org.sonar.api.i18n.I18n;
-import org.sonar.api.i18n.LanguagePack;
-import org.sonar.api.platform.PluginRepository;
-import org.sonar.api.utils.Logs;
-import org.sonar.api.utils.SonarException;
-
-import java.io.InputStream;
-import java.net.URI;
-import java.net.URL;
-import java.net.URLClassLoader;
-import java.text.MessageFormat;
-import java.util.*;
-
-public final class I18nManager implements I18n, ServerExtension, BatchExtension {
-
-  private static final Logger LOG = LoggerFactory.getLogger(I18nManager.class);
-  public static final String packagePathToSearchIn = "org/sonar/i18n";
-
-  private PluginRepository pluginRepository;
-  private LanguagePack[] languagePacks;
-  private Map<String, String> keys = Maps.newHashMap();
-  private Properties unknownKeys = new Properties();
-  private BundleClassLoader bundleClassLoader = new BundleClassLoader();
-
-  public I18nManager(PluginRepository pluginRepository, LanguagePack[] languagePacks) {
-    this.pluginRepository = pluginRepository;
-    this.languagePacks = languagePacks;
-  }
-
-  public I18nManager(PluginRepository pluginRepository) {
-    this(pluginRepository, new LanguagePack[0]);
-  }
-
-
-  public void start() {
-    doStart(InstalledPlugin.create(pluginRepository));
-  }
-
-  void doStart(List<InstalledPlugin> installedPlugins) {
-    Logs.INFO.info("Loading i18n bundles");
-    Set<URI> alreadyLoadedResources = Sets.newHashSet();
-    for (InstalledPlugin plugin : installedPlugins) {
-      searchAndStoreBundleNames(plugin.key, plugin.classloader, alreadyLoadedResources);
-    }
-    for (LanguagePack pack : languagePacks) {
-      addLanguagePack(pack);
-    }
-  }
-
-  private void addLanguagePack(LanguagePack languagePack) {
-    LOG.debug("Search for bundles in language pack : {}", languagePack);
-    for (String pluginKey : languagePack.getPluginKeys()) {
-      String bundleBaseName = buildBundleBaseName(pluginKey);
-      for (Locale locale : languagePack.getLocales()) {
-        String bundlePropertiesFile = new StringBuilder(bundleBaseName).append('_').append(locale.toString()).append(".properties").toString();
-        ClassLoader classloader = languagePack.getClass().getClassLoader();
-        LOG.info("Adding locale {} for bundleName : {} from classloader : {}", new Object[]{locale, bundleBaseName, classloader});
-        bundleClassLoader.addResource(bundlePropertiesFile, classloader);
-      }
-    }
-  }
-
-  private String buildBundleBaseName(String pluginKey) {
-    return packagePathToSearchIn + "/" + pluginKey;
-  }
-
-  private void searchAndStoreBundleNames(String pluginKey, ClassLoader classloader, Set<URI> alreadyLoadedResources) {
-    String bundleBaseName = buildBundleBaseName(pluginKey);
-    String bundleDefaultPropertiesFile = bundleBaseName + ".properties";
-    try {
-      LOG.debug("Search for ResourceBundle base file '" + bundleDefaultPropertiesFile + "' in the classloader : " + classloader);
-      List<URL> resources = EnumerationUtils.toList(classloader.getResources(bundleDefaultPropertiesFile));
-      if (resources.size() > 0) {
-        if (resources.size() > 1) {
-          LOG.warn("File '{}' found several times in the classloader : {}. Only the first one will be taken in account.",
-              bundleDefaultPropertiesFile, classloader);
-        }
-
-        URL propertiesUrl = resources.get(0);
-        if (!alreadyLoadedResources.contains(propertiesUrl.toURI())) {
-          LOG.debug("Found the ResourceBundle base file : {} from classloader : {}", propertiesUrl, classloader);
-          LOG.info("Add bundleName : {} from classloader : {}", bundleBaseName, classloader);
-          bundleClassLoader.addResource(bundleDefaultPropertiesFile, classloader);
-          alreadyLoadedResources.add(propertiesUrl.toURI());
-
-          Properties bundleContent = new Properties();
-          InputStream input = null;
-          try {
-            input = propertiesUrl.openStream();
-            bundleContent.load(input);
-            Enumeration<String> keysToAdd = (Enumeration<String>) bundleContent.propertyNames();
-            while (keysToAdd.hasMoreElements()) {
-              String key = keysToAdd.nextElement();
-              if (keys.containsKey(key)) {
-                LOG.warn("DUPLICATE KEY : Key '{}' defined in bundle '{}' is already defined in bundle '{}'. It is ignored.", new Object[]{
-                    key, bundleBaseName, keys.get(key)});
-              } else {
-                keys.put(key, bundleBaseName);
-              }
-            }
-          } finally {
-            IOUtils.closeQuietly(input);
-          }
-        }
-      }
-    } catch (Exception e) {
-      LOG.error("Fail to load '" + bundleDefaultPropertiesFile + "' in classloader : " + classloader, e);
-      throw new SonarException("Fail to load '" + bundleDefaultPropertiesFile + "' in classloader : " + classloader, e);
-    }
-  }
-
-  public String message(final Locale locale, final String key, final String defaultText, final Object... objects) {
-    String result = defaultText;
-    try {
-      String bundleBaseName = keys.get(key);
-      if (bundleBaseName == null) {
-        LOG.warn("UNKNOWN KEY : Key '{}' not found in any bundle. Default value '{}' is returned.", key, defaultText);
-        unknownKeys.put(key, defaultText);
-      } else {
-        try {
-          ResourceBundle bundle = ResourceBundle.getBundle(bundleBaseName, locale, bundleClassLoader);
-
-          String value = bundle.getString(key);
-          if ("".equals(value)) {
-            LOG.warn("VOID KEY : Key '{}' (from bundle '{}') returns a void value. Default value '{}' is returned.", new Object[]{key,
-                bundleBaseName, defaultText});
-          } else {
-            result = value;
-          }
-        } catch (MissingResourceException e) {
-          LOG.warn("BUNDLE NOT LOADED : Failed loading bundle {} from classloader {}. Default value '{}' is returned.", new Object[]{
-              bundleBaseName, bundleClassLoader, defaultText});
-        }
-      }
-    } catch (Exception e) {
-      LOG.error("Exception when retrieving I18n string.", e);
-    }
-
-    if (objects.length > 0) {
-      LOG.debug("Translation : {}, {}, {}, {}", new String[]{locale.toString(), key, defaultText, Arrays.deepToString(objects)});
-      return MessageFormat.format(result, objects);
-    } else {
-      return result;
-    }
-  }
-
-  /**
-   * @return the unknownKeys
-   */
-  public Properties getUnknownKeys() {
-    return unknownKeys;
-  }
-
-
-  private static class BundleClassLoader extends URLClassLoader {
-    private Map<String, ClassLoader> resources = Maps.newHashMap();
-
-    public BundleClassLoader() {
-      super(new URL[]{}, null);
-    }
-
-    public void addResource(String resourceName, ClassLoader classloader) {
-      resources.put(resourceName, classloader);
-    }
-
-    @Override
-    public URL findResource(String name) {
-      if (resources.containsKey(name)) {
-        return resources.get(name).getResource(name);
-      }
-      return null;
-    }
-  }
-}
diff --git a/plugins/sonar-i18n-plugin/src/main/java/org/sonar/plugins/i18n/I18nPlugin.java b/plugins/sonar-i18n-plugin/src/main/java/org/sonar/plugins/i18n/I18nPlugin.java
deleted file mode 100644 (file)
index 0e6495c..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-/*\r
- * Sonar, open source software quality management tool.\r
- * Copyright (C) 2008-2011 SonarSource\r
- * mailto:contact AT sonarsource DOT com\r
- *\r
- * Sonar is free software; you can redistribute it and/or\r
- * modify it under the terms of the GNU Lesser General Public\r
- * License as published by the Free Software Foundation; either\r
- * version 3 of the License, or (at your option) any later version.\r
- *\r
- * Sonar is distributed in the hope that it will be useful,\r
- * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
- * Lesser General Public License for more details.\r
- *\r
- * You should have received a copy of the GNU Lesser General Public\r
- * License along with Sonar; if not, write to the Free Software\r
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02\r
- */\r
-package org.sonar.plugins.i18n;\r
-\r
-import org.sonar.api.SonarPlugin;\r
-\r
-import java.util.Arrays;\r
-import java.util.List;\r
-\r
-/**\r
- * @since 2.9\r
- */\r
-public final class I18nPlugin extends SonarPlugin {\r
-\r
-  public List getExtensions() {\r
-    return Arrays.asList(I18nManager.class, I18nWebService.class);\r
-  }\r
-\r
-}\r
diff --git a/plugins/sonar-i18n-plugin/src/main/java/org/sonar/plugins/i18n/InstalledPlugin.java b/plugins/sonar-i18n-plugin/src/main/java/org/sonar/plugins/i18n/InstalledPlugin.java
deleted file mode 100644 (file)
index d0348df..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Sonar, open source software quality management tool.
- * Copyright (C) 2008-2011 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * Sonar is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * Sonar is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with Sonar; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
- */
-package org.sonar.plugins.i18n;
-
-import com.google.common.collect.Lists;
-import org.sonar.api.Plugin;
-import org.sonar.api.platform.PluginMetadata;
-import org.sonar.api.platform.PluginRepository;
-
-import java.util.List;
-
-class InstalledPlugin {
-  String key;
-  ClassLoader classloader;
-
-  InstalledPlugin(String key, ClassLoader classloader) {
-    this.key = key;
-    this.classloader = classloader;
-  }
-
-  static List<InstalledPlugin> create(PluginRepository pluginRepository) {
-    List<InstalledPlugin> result = Lists.newArrayList();
-    for (PluginMetadata metadata : pluginRepository.getMetadata()) {
-      Plugin entryPoint = pluginRepository.getPlugin(metadata.getKey());
-      result.add(new InstalledPlugin(metadata.getKey(), entryPoint.getClass().getClassLoader()));
-    }
-    return result;
-  }
-}
diff --git a/plugins/sonar-i18n-plugin/src/test/java/org/sonar/plugins/i18n/I18nManagerTest.java b/plugins/sonar-i18n-plugin/src/test/java/org/sonar/plugins/i18n/I18nManagerTest.java
deleted file mode 100644 (file)
index f4d0757..0000000
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * Sonar, open source software quality management tool.
- * Copyright (C) 2008-2011 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * Sonar is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * Sonar is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with Sonar; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
- */
-package org.sonar.plugins.i18n;
-
-import com.google.common.collect.Lists;
-import org.junit.Before;
-import org.junit.Test;
-import org.sonar.api.i18n.LanguagePack;
-import org.sonar.api.platform.PluginRepository;
-
-import java.net.URL;
-import java.net.URLClassLoader;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Locale;
-
-import static org.junit.Assert.assertEquals;
-import static org.mockito.Mockito.mock;
-
-public class I18nManagerTest {
-
-  public static String TEST_PLUGIN_CLASS_NAME = "org.sonar.plugins.i18n.utils.StandardPlugin";
-  public static String FRENCH_PACK_CLASS_NAME = "org.sonar.plugins.i18n.utils.FrenchLanguagePack";
-  public static String QUEBEC_PACK_CLASS_NAME = "org.sonar.plugins.i18n.utils.QuebecLanguagePack";
-
-  private static URL classSource = I18nManagerTest.class.getProtectionDomain().getCodeSource().getLocation();
-  private I18nManager manager;
-
-  @Before
-  public void createManager() throws Exception {
-    List<InstalledPlugin> plugins = Lists.newArrayList(
-        new InstalledPlugin("test", new TestClassLoader(getClass().getClassLoader().getResource("StandardPlugin.jar"))),
-        new InstalledPlugin("fake1", getClass().getClassLoader()),
-        new InstalledPlugin("fake2", getClass().getClassLoader())
-    );
-
-    TestClassLoader frenchPackClassLoader = new TestClassLoader(getClass().getClassLoader().getResource("FrenchPlugin.jar"));
-    LanguagePack frenchPack = (LanguagePack) frenchPackClassLoader.loadClass(FRENCH_PACK_CLASS_NAME).newInstance();
-
-    TestClassLoader quebecPackClassLoader = new TestClassLoader(getClass().getClassLoader().getResource("QuebecPlugin.jar"));
-    LanguagePack quebecPack = (LanguagePack) quebecPackClassLoader.loadClass(QUEBEC_PACK_CLASS_NAME).newInstance();
-
-    manager = new I18nManager(mock(PluginRepository.class), new LanguagePack[]{frenchPack, quebecPack});
-    manager.doStart(plugins);
-  }
-
-  @Test
-  public void shouldTranslateWithoutRegionalVariant() {
-    List<String> sentence = Arrays.asList("it", "is", "cold");
-    String result = "";
-    for (String token : sentence) {
-      result += manager.message(Locale.FRENCH, token, token) + " ";
-    }
-    assertEquals("Il fait froid ", result);
-  }
-
-  @Test
-  public void shouldTranslateWithRegionalVariant() {
-    // it & is are taken from the french language pack
-    // and cold is taken from the quebec language pack
-    List<String> sentence = Arrays.asList("it", "is", "cold");
-    String result = "";
-    for (String token : sentence) {
-      result += manager.message(Locale.CANADA_FRENCH, token, token) + " ";
-    }
-    assertEquals("Il fait frette ", result);
-  }
-
-  @Test
-  public void shouldTranslateReturnsDefaultBundleValue() {
-    String result = manager.message(Locale.FRENCH, "only.english", "Default");
-    assertEquals("Ketchup", result);
-  }
-
-  @Test
-  public void shouldTranslateUnknownValue() {
-    String result = manager.message(Locale.FRENCH, "unknown", "Default value for Unknown");
-    assertEquals("Default value for Unknown", result);
-    assertEquals(1, manager.getUnknownKeys().size());
-    assertEquals("Default value for Unknown", manager.getUnknownKeys().getProperty("unknown"));
-  }
-
-  public static class TestClassLoader extends URLClassLoader {
-    public TestClassLoader(URL url) {
-      super(new URL[]{url, classSource}, Thread.currentThread().getContextClassLoader());
-    }
-
-    protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
-      Class c = findLoadedClass(name);
-      if (c == null) {
-        if (name.equals(TEST_PLUGIN_CLASS_NAME) || name.equals(QUEBEC_PACK_CLASS_NAME) || name.equals(FRENCH_PACK_CLASS_NAME)) {
-          c = findClass(name);
-        } else {
-          return super.loadClass(name, resolve);
-        }
-      }
-      if (resolve) {
-        resolveClass(c);
-      }
-      return c;
-    }
-  }
-
-}
diff --git a/plugins/sonar-i18n-plugin/src/test/java/org/sonar/plugins/i18n/I18nPluginTest.java b/plugins/sonar-i18n-plugin/src/test/java/org/sonar/plugins/i18n/I18nPluginTest.java
deleted file mode 100644 (file)
index 9fc0071..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Sonar, open source software quality management tool.
- * Copyright (C) 2008-2011 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * Sonar is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * Sonar is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with Sonar; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
- */
-package org.sonar.plugins.i18n;
-
-import org.junit.Test;
-
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.number.OrderingComparisons.greaterThanOrEqualTo;
-
-public class I18nPluginTest {
-
-  @Test
-  public void shouldGetExtensions() {
-    assertThat(new I18nPlugin().getExtensions().size(), greaterThanOrEqualTo(2));
-  }
-}
diff --git a/plugins/sonar-i18n-plugin/src/test/java/org/sonar/plugins/i18n/utils/FrenchLanguagePack.java b/plugins/sonar-i18n-plugin/src/test/java/org/sonar/plugins/i18n/utils/FrenchLanguagePack.java
deleted file mode 100644 (file)
index 98c0978..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Sonar, open source software quality management tool.
- * Copyright (C) 2008-2011 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * Sonar is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * Sonar is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with Sonar; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
- */
-package org.sonar.plugins.i18n.utils;
-
-import org.sonar.api.i18n.LanguagePack;
-
-import java.util.Arrays;
-import java.util.List;
-import java.util.Locale;
-
-public class FrenchLanguagePack extends LanguagePack {
-
-  @Override
-  public List<String> getPluginKeys() {
-    return Arrays.asList("test");
-  }
-
-  @Override
-  public List<Locale> getLocales() {
-    return Arrays.asList(Locale.FRENCH);
-  }
-}
\ No newline at end of file
diff --git a/plugins/sonar-i18n-plugin/src/test/java/org/sonar/plugins/i18n/utils/QuebecLanguagePack.java b/plugins/sonar-i18n-plugin/src/test/java/org/sonar/plugins/i18n/utils/QuebecLanguagePack.java
deleted file mode 100644 (file)
index 5e9913e..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Sonar, open source software quality management tool.
- * Copyright (C) 2008-2011 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * Sonar is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * Sonar is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with Sonar; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
- */
-package org.sonar.plugins.i18n.utils;
-
-import org.sonar.api.i18n.LanguagePack;
-
-import java.util.Arrays;
-import java.util.List;
-import java.util.Locale;
-
-public class QuebecLanguagePack extends LanguagePack {
-
-  @Override
-  public List<String> getPluginKeys() {
-    return Arrays.asList("test");
-  }
-
-  @Override
-  public List<Locale> getLocales() {
-    return Arrays.asList(Locale.CANADA_FRENCH);
-  }
-}
\ No newline at end of file
diff --git a/plugins/sonar-i18n-plugin/src/test/resources/FrenchPlugin.jar b/plugins/sonar-i18n-plugin/src/test/resources/FrenchPlugin.jar
deleted file mode 100644 (file)
index ab61bd8..0000000
Binary files a/plugins/sonar-i18n-plugin/src/test/resources/FrenchPlugin.jar and /dev/null differ
diff --git a/plugins/sonar-i18n-plugin/src/test/resources/FrenchPlugin/META-INF/MANIFEST.MF b/plugins/sonar-i18n-plugin/src/test/resources/FrenchPlugin/META-INF/MANIFEST.MF
deleted file mode 100644 (file)
index a45029d..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-Manifest-Version: 1.0\r
-Created-By: 1.6.0_17 (Apple Inc.)\r
-\r
diff --git a/plugins/sonar-i18n-plugin/src/test/resources/FrenchPlugin/org/sonar/i18n/test_fr.properties b/plugins/sonar-i18n-plugin/src/test/resources/FrenchPlugin/org/sonar/i18n/test_fr.properties
deleted file mode 100644 (file)
index cc8efa7..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-it=Il
-is=fait
-cold=froid
-
diff --git a/plugins/sonar-i18n-plugin/src/test/resources/QuebecPlugin.jar b/plugins/sonar-i18n-plugin/src/test/resources/QuebecPlugin.jar
deleted file mode 100644 (file)
index 101df34..0000000
Binary files a/plugins/sonar-i18n-plugin/src/test/resources/QuebecPlugin.jar and /dev/null differ
diff --git a/plugins/sonar-i18n-plugin/src/test/resources/QuebecPlugin/META-INF/MANIFEST.MF b/plugins/sonar-i18n-plugin/src/test/resources/QuebecPlugin/META-INF/MANIFEST.MF
deleted file mode 100644 (file)
index a45029d..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-Manifest-Version: 1.0\r
-Created-By: 1.6.0_17 (Apple Inc.)\r
-\r
diff --git a/plugins/sonar-i18n-plugin/src/test/resources/QuebecPlugin/org/sonar/i18n/test_fr_CA.properties b/plugins/sonar-i18n-plugin/src/test/resources/QuebecPlugin/org/sonar/i18n/test_fr_CA.properties
deleted file mode 100644 (file)
index 38b5b84..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-cold=frette
-
diff --git a/plugins/sonar-i18n-plugin/src/test/resources/StandardPlugin.jar b/plugins/sonar-i18n-plugin/src/test/resources/StandardPlugin.jar
deleted file mode 100644 (file)
index 73deac7..0000000
Binary files a/plugins/sonar-i18n-plugin/src/test/resources/StandardPlugin.jar and /dev/null differ
diff --git a/plugins/sonar-i18n-plugin/src/test/resources/StandardPlugin/META-INF/MANIFEST.MF b/plugins/sonar-i18n-plugin/src/test/resources/StandardPlugin/META-INF/MANIFEST.MF
deleted file mode 100644 (file)
index a45029d..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-Manifest-Version: 1.0\r
-Created-By: 1.6.0_17 (Apple Inc.)\r
-\r
diff --git a/plugins/sonar-i18n-plugin/src/test/resources/StandardPlugin/org/sonar/i18n/test.properties b/plugins/sonar-i18n-plugin/src/test/resources/StandardPlugin/org/sonar/i18n/test.properties
deleted file mode 100644 (file)
index 88aabfe..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-it=It
-is=is
-cold=cold
-only.english=Ketchup
diff --git a/plugins/sonar-i18n-plugin/src/test/resources/logback-test.xml b/plugins/sonar-i18n-plugin/src/test/resources/logback-test.xml
deleted file mode 100644 (file)
index cdc8866..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-
-<configuration>
-  <appender name="STDOUT"
-            class="ch.qos.logback.core.ConsoleAppender">
-    <layout class="ch.qos.logback.classic.PatternLayout">
-      <pattern>
-        %d{HH:mm:ss.SSS} %-5level %logger{36} - %msg%n
-      </pattern>
-    </layout>
-  </appender>
-
-  <root>
-    <level value="INFO"/>
-    <appender-ref ref="STDOUT"/>
-  </root>
-</configuration>
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index fb95f636230b66eebf448037088630079b9c5950..5e43e693a058d49db7bd34310ba59ced86119542 100644 (file)
--- a/pom.xml
+++ b/pom.xml
@@ -48,7 +48,6 @@
     <module>plugins/sonar-cpd-plugin</module>
     <module>plugins/sonar-squid-java-plugin</module>
     <module>plugins/sonar-design-plugin</module>
-    <module>plugins/sonar-i18n-plugin</module>
   </modules>
 
   <organization>
index da36b40433563762f4e18dff6d349c17b7945692..6e83c28617d94f337d6f1484dc0d6aa15cffd8d2 100644 (file)
       <version>${project.version}</version>
       <scope>runtime</scope>
     </dependency>
-    <dependency>
-      <groupId>org.codehaus.sonar.plugins</groupId>
-      <artifactId>sonar-i18n-plugin</artifactId>
-      <version>${project.version}</version>
-      <scope>runtime</scope>
-    </dependency>
     <dependency>
       <groupId>org.sonatype.jsw-binaries</groupId>
       <artifactId>jsw-binaries</artifactId>
index c11fca34b93398c13db262e83ee72490aea3dba1..50b83f801222e7ebae01951c6e7bec4246afc2ff 100644 (file)
     <level value="INFO"/>
   </logger>
 
-  <logger name="org.sonar.plugins.i18n.I18nManager">
+  <logger name="org.sonar.plugins.core.i18n.I18nManager">
     <level value="INFO"/>
   </logger>
 
index e8d698d29596269869336417002dc1b556abe1a6..249c94b79b7d65278df836baf01e4e05a3a05c12 100644 (file)
@@ -42,6 +42,7 @@ import java.util.*;
 public class BatchPluginRepository implements PluginRepository {
 
   private static final Logger LOG = LoggerFactory.getLogger(BatchPluginRepository.class);
+  private static final String CORE_PLUGIN = "core";
 
   private ArtifactDownloader artifactDownloader;
   private Map<String, Plugin> pluginsByKey;
@@ -52,12 +53,12 @@ public class BatchPluginRepository implements PluginRepository {
 
   public BatchPluginRepository(ArtifactDownloader artifactDownloader, Configuration configuration) {
     this.artifactDownloader = artifactDownloader;
-    if (configuration.getString(CoreProperties.INCLUDE_PLUGINS) != null) {
-      whiteList = Sets.newTreeSet(Arrays.asList(configuration.getStringArray(CoreProperties.INCLUDE_PLUGINS)));
+    if (configuration.getString(CoreProperties.BATCH_INCLUDE_PLUGINS) != null) {
+      whiteList = Sets.newTreeSet(Arrays.asList(configuration.getStringArray(CoreProperties.BATCH_INCLUDE_PLUGINS)));
       LOG.info("Include plugins: " + Joiner.on(", ").join(whiteList));
     }
-    if (configuration.getString(CoreProperties.EXCLUDE_PLUGINS) != null) {
-      blackList = Sets.newTreeSet(Arrays.asList(configuration.getStringArray(CoreProperties.EXCLUDE_PLUGINS)));
+    if (configuration.getString(CoreProperties.BATCH_EXCLUDE_PLUGINS) != null) {
+      blackList = Sets.newTreeSet(Arrays.asList(configuration.getStringArray(CoreProperties.BATCH_EXCLUDE_PLUGINS)));
       LOG.info("Exclude plugins: " + Joiner.on(", ").join(blackList));
     }
     // TODO reactivate somewhere else:  LOG.info("Execution environment: {} {}", environment.getKey(), environment.getVersion());
@@ -124,6 +125,9 @@ public class BatchPluginRepository implements PluginRepository {
   }
 
   boolean isAccepted(String pluginKey) {
+    if (CORE_PLUGIN.equals(pluginKey)) {
+      return true;
+    }
     if (whiteList != null) {
       return whiteList.contains(pluginKey);
     }
index 82bbf91ed333556146d238c5c27ea8b9d5ede939..00b291a22796f31db4a8a820ab4783af3bc524d3 100644 (file)
@@ -118,7 +118,7 @@ public class BatchPluginRepositoryTest {
     when(downloader.downloadPlugin(checkstyleExt)).thenReturn(copyFiles("sonar-checkstyle-extensions-plugin-0.1-SNAPSHOT.jar"));
 
     PropertiesConfiguration conf = new PropertiesConfiguration();
-    conf.setProperty(CoreProperties.EXCLUDE_PLUGINS, "checkstyle");
+    conf.setProperty(CoreProperties.BATCH_EXCLUDE_PLUGINS, "checkstyle");
     repository = new BatchPluginRepository(downloader, conf);
 
     repository.doStart(Arrays.asList(checkstyle, checkstyleExt));
@@ -149,17 +149,33 @@ public class BatchPluginRepositoryTest {
   @Test
   public void whiteListShouldTakePrecedenceOverBlackList() {
     PropertiesConfiguration conf = new PropertiesConfiguration();
-    conf.setProperty(CoreProperties.INCLUDE_PLUGINS, "checkstyle,pmd,findbugs");
-    conf.setProperty(CoreProperties.EXCLUDE_PLUGINS, "cobertura,pmd");
+    conf.setProperty(CoreProperties.BATCH_INCLUDE_PLUGINS, "checkstyle,pmd,findbugs");
+    conf.setProperty(CoreProperties.BATCH_EXCLUDE_PLUGINS, "cobertura,pmd");
     repository = new BatchPluginRepository(mock(ArtifactDownloader.class), conf);
 
     assertThat(repository.isAccepted("pmd"), Matchers.is(true));
   }
 
+  @Test
+  public void corePluginShouldAlwaysBeInWhiteList() {
+    PropertiesConfiguration conf = new PropertiesConfiguration();
+    conf.setProperty(CoreProperties.BATCH_INCLUDE_PLUGINS, "checkstyle,pmd,findbugs");
+    repository = new BatchPluginRepository(mock(ArtifactDownloader.class), conf);
+    assertThat(repository.isAccepted("core"), Matchers.is(true));
+  }
+
+  @Test
+  public void corePluginShouldNeverBeInBlackList() {
+    PropertiesConfiguration conf = new PropertiesConfiguration();
+    conf.setProperty(CoreProperties.BATCH_EXCLUDE_PLUGINS, "core,findbugs");
+    repository = new BatchPluginRepository(mock(ArtifactDownloader.class), conf);
+    assertThat(repository.isAccepted("core"), Matchers.is(true));
+  }
+
   @Test
   public void shouldCheckWhitelist() {
     PropertiesConfiguration conf = new PropertiesConfiguration();
-    conf.setProperty(CoreProperties.INCLUDE_PLUGINS, "checkstyle,pmd,findbugs");
+    conf.setProperty(CoreProperties.BATCH_INCLUDE_PLUGINS, "checkstyle,pmd,findbugs");
     repository = new BatchPluginRepository(mock(ArtifactDownloader.class), conf);
 
     assertThat(repository.isAccepted("checkstyle"), Matchers.is(true));
@@ -170,7 +186,7 @@ public class BatchPluginRepositoryTest {
   @Test
   public void shouldCheckBlackListIfNoWhiteList() {
     PropertiesConfiguration conf = new PropertiesConfiguration();
-    conf.setProperty(CoreProperties.EXCLUDE_PLUGINS, "checkstyle,pmd,findbugs");
+    conf.setProperty(CoreProperties.BATCH_EXCLUDE_PLUGINS, "checkstyle,pmd,findbugs");
     repository = new BatchPluginRepository(mock(ArtifactDownloader.class), conf);
 
     assertThat(repository.isAccepted("checkstyle"), Matchers.is(false));
index e19a5a3f13b9344d0119bedef3197abe9f22319b..01c652308cb6b9b1f3c67ae12fe930f64adb6f28 100644 (file)
@@ -103,8 +103,8 @@ public interface CoreProperties {
   String SERVER_STARTTIME = "sonar.core.startTime"; // format is yyyy-MM-dd'T'HH:mm:ssZ
   String SKIP_TENDENCIES_PROPERTY = "sonar.skipTendencies";
   boolean SKIP_TENDENCIES_DEFAULT_VALUE = false;
-  String INCLUDE_PLUGINS = "sonar.includePlugins";
-  String EXCLUDE_PLUGINS = "sonar.excludePlugins";
+  String BATCH_INCLUDE_PLUGINS = "sonar.includePlugins";
+  String BATCH_EXCLUDE_PLUGINS = "sonar.excludePlugins";
 
   /* CPD */
   String CPD_PLUGIN = "cpd";
index 7d1fe84dcdc8de5c351368fc909c8078c93f7fa2..5205ad689de39e7ba1c5bca8c6c0a050a17ec906 100644 (file)
           <version>${project.version}</version>
           <scope>provided</scope>
         </dependency>
-        <dependency>
-          <groupId>org.codehaus.sonar.plugins</groupId>
-          <artifactId>sonar-i18n-plugin</artifactId>
-          <version>${project.version}</version>
-          <scope>provided</scope>
-        </dependency>
       </dependencies>
     </profile>
 
diff --git a/sonar-server/src/main/webapp/WEB-INF/app/controllers/i18n_controller.rb b/sonar-server/src/main/webapp/WEB-INF/app/controllers/i18n_controller.rb
new file mode 100644 (file)
index 0000000..30ba451
--- /dev/null
@@ -0,0 +1,64 @@
+#
+# Sonar, entreprise quality control tool.
+# Copyright (C) 2008-2011 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# Sonar is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# Sonar is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with Sonar; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
+#
+
+require "json"
+
+class I18nController < Api::ApiController
+
+  def index
+      render :text => "Use one of the following:<br><ul>" +
+                      "<li>/i18n/unknown_keys?format=text|json</li>" +
+                      "</ul>"
+  end
+
+  #
+  # GET /i18n/unknown_keys
+  #
+  def unknown_keys
+    begin
+      output = ""
+      properties = i18n_manager.unknown_keys
+
+      properties.keys.sort.each {|key| output += "#{key}=#{properties[key]}\n" }
+
+      output = "# No unknown keys" if output.empty?
+
+      respond_to do |format|
+        format.json { render :json => JSON(properties) }
+        format.xml  { render :xml => xml_not_supported }
+        format.text { render :text => output }
+      end
+
+    rescue ApiException => e
+      render_error(e.msg, e.code)
+
+    rescue Exception => e
+      logger.error("Fails to execute #{request.url} : #{e.message}")
+      render_error(e.message)
+    end
+  end
+
+  private
+
+  def i18n_manager
+    java_facade.getComponentByClassname('i18n', 'org.sonar.plugins.core.i18n.I18nManager')
+  end
+
+end
\ No newline at end of file