001/*
002 * Sonar, open source software quality management tool.
003 * Copyright (C) 2008-2012 SonarSource
004 * mailto:contact AT sonarsource DOT com
005 *
006 * Sonar is free software; you can redistribute it and/or
007 * modify it under the terms of the GNU Lesser General Public
008 * License as published by the Free Software Foundation; either
009 * version 3 of the License, or (at your option) any later version.
010 *
011 * Sonar is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014 * Lesser General Public License for more details.
015 *
016 * You should have received a copy of the GNU Lesser General Public
017 * License along with Sonar; if not, write to the Free Software
018 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
019 */
020package org.sonar.test.i18n;
021
022import com.google.common.collect.Maps;
023import org.apache.commons.io.FileUtils;
024import org.apache.commons.lang.StringUtils;
025import org.sonar.test.TestUtils;
026
027import java.io.File;
028import java.net.URI;
029import java.net.URISyntaxException;
030import java.util.Collection;
031import java.util.Map;
032
033import static org.junit.Assert.assertThat;
034import static org.junit.Assert.fail;
035
036public final class I18nMatchers {
037
038  private I18nMatchers() {
039  }
040
041  /**
042   * <p>
043   * <b>Used by language packs that translate Core bundles.</b>
044   * </p>
045   * Returns a matcher which checks that a translation bundle is up to date with the corresponding English Core bundle.
046   * <ul>
047   * <li>If a version of Sonar is specified, then the check is done against this version of the bundle found on Sonar Github repository.</li>
048   * <li>If sonarVersion is set to NULL, the check is done against the latest version of this bundle found on Github (master branch).</li>
049   * </ul>
050   * 
051   * @param sonarVersion
052   *          the version of the bundle to check against, or NULL to check against the latest source on GitHub
053   * @return the matcher
054   */
055  public static BundleSynchronizedMatcher isBundleUpToDate(String sonarVersion) {
056    return new BundleSynchronizedMatcher(sonarVersion);
057  }
058
059  /**
060   * <p>
061   * <b>Used by language packs that translate third-party bundles.</b>
062   * </p>
063   * Returns a matcher which checks that a translation bundle is up to date with the given reference English bundle from a third-party plugin.
064   * 
065   * @param referenceEnglishBundleURI
066   *          the URI referencing the English bundle to check against
067   * @return the matcher
068   */
069  public static BundleSynchronizedMatcher isBundleUpToDate(URI referenceEnglishBundleURI) {
070    return new BundleSynchronizedMatcher(referenceEnglishBundleURI);
071  }
072
073  /**
074   * <p>
075   * <b>Used only by independent plugins that embeds their own bundles for every language.</b>
076   * </p>
077   * Returns a matcher which checks that a translation bundle is up to date with the corresponding default one found in the same folder.
078   * 
079   * @return the matcher
080   */
081  public static BundleSynchronizedMatcher isBundleUpToDate() {
082    return new BundleSynchronizedMatcher();
083  }
084
085  /**
086   * <p>
087   * <b>Must be used only by independent plugins that embeds their own bundles for every language.</b>
088   * </p>
089   * Checks that all the translation bundles found on the classpath are up to date with the corresponding default one found in the same
090   * folder.
091   */
092  public static void assertAllBundlesUpToDate() {
093    try {
094      assertAllBundlesUpToDate(null, null);
095    } catch (URISyntaxException e) {
096      // Ignore, this can't happen here
097    }
098  }
099
100  /**
101   * <p>
102   * <b>Must be used only by language packs.</b>
103   * </p>
104   * <p>
105   * Depending on the parameters, this method does the following:
106   * <ul>
107   * <li><b>sonarVersion</b>: checks that all the Core translation bundles found on the classpath are up to date with the corresponding English ones found on Sonar 
108   * GitHub repository for the given Sonar version.
109   * <ul><li><i>Note: if sonarVersion is set to NULL, the check is done against the latest version of this bundles found the master branch of the GitHub repository.</i></li></ul>
110   * </li>
111   * <li><b>pluginIdsToBundleUrlMap</b>: checks that other translation bundles found on the classpath are up to date with the reference English bundles of the corresponding
112   * plugins given by the "pluginIdsToBundleUrlMap" parameter.
113   * </li>
114   * </ul>
115   * </p>
116   * <p><br>
117   * The following example will check that the translation of the Core bundles are up to date with version 3.2 of Sonar English Language Pack, and it
118   * will also check that the translation of the bundle of the Web plugin is up to date with the reference English bundle of version 1.2 of the Web plugin:
119   * <pre>
120   * Map<String, String> pluginIdsToBundleUrlMap = Maps.newHashMap();
121   * pluginIdsToBundleUrlMap.put("web", "http://svn.codehaus.org/sonar-plugins/tags/sonar-web-plugin-1.2/src/main/resources/org/sonar/l10n/web.properties");
122   * assertAllBundlesUpToDate("3.2", pluginIdsToBundleUrlMap);
123   * </pre>
124   * </p>
125   * 
126   * @param sonarVersion
127   *          the version of the bundles to check against, or NULL to check against the latest source on GitHub
128   * @param pluginIdsToBundleUrlMap
129   *          a map that gives, for a given plugin, the URL of the English bundle that must be used to check the translation.
130   * @throws URISyntaxException if the provided URLs in the "pluginIdsToBundleUrlMap" parameter are not correct
131   */
132  public static void assertAllBundlesUpToDate(String sonarVersion, Map<String, String> pluginIdsToBundleUrlMap) throws URISyntaxException {
133    File bundleFolder = TestUtils.getResource(BundleSynchronizedMatcher.L10N_PATH);
134    if (bundleFolder == null || !bundleFolder.isDirectory()) {
135      fail("No bundle found in '" + BundleSynchronizedMatcher.L10N_PATH + "'");
136    }
137
138    Collection<File> bundles = FileUtils.listFiles(bundleFolder, new String[] {"properties"}, false);
139    Map<String, String> failedAssertionMessages = Maps.newHashMap();
140    for (File bundle : bundles) {
141      String bundleName = bundle.getName();
142      if (bundleName.indexOf('_') > 0) {
143        try {
144          String baseBundleName = BundleSynchronizedMatcher.extractDefaultBundleName(bundleName);
145          String pluginId = StringUtils.substringBefore(baseBundleName, ".");
146          if (BundleSynchronizedMatcher.isCoreBundle(baseBundleName)) {
147            // this is a core bundle => must be checked againt the provided version of Sonar
148            assertThat(bundleName, isBundleUpToDate(sonarVersion));
149          } else if (pluginIdsToBundleUrlMap != null && pluginIdsToBundleUrlMap.get(pluginId) != null) {
150            // this is a third-party plugin translated by a language pack => must be checked against the provided URL
151            assertThat(bundleName, isBundleUpToDate(new URI(pluginIdsToBundleUrlMap.get(pluginId))));
152          } else {
153            // this is the case of a plugin that provides all the bundles for every language => check the bundles inside the plugin
154            assertThat(bundleName, isBundleUpToDate());
155          }
156        } catch (AssertionError e) {
157          failedAssertionMessages.put(bundleName, e.getMessage());
158        }
159      }
160    }
161
162    if (!failedAssertionMessages.isEmpty()) {
163      StringBuilder message = new StringBuilder();
164      message.append(failedAssertionMessages.size());
165      message.append(" bundles are not up-to-date: ");
166      message.append(StringUtils.join(failedAssertionMessages.keySet(), ", "));
167      message.append("\n\n");
168      message.append(StringUtils.join(failedAssertionMessages.values(), "\n\n"));
169      fail(message.toString());
170    }
171  }
172}