blob: 050ee244f18e356a3f4fc7cc2ba6c33c3e3f000a [file] [log] [blame]
/*
* Copyright (c) 2020 Eclipse contributors and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v20.html
*/
package org.eclipse.oomph.internal.ui;
import org.eclipse.oomph.util.IORuntimeException;
import org.eclipse.oomph.util.IOUtil;
import org.eclipse.oomph.util.PropertiesUtil;
import org.eclipse.oomph.util.ReflectUtil;
import org.eclipse.emf.common.CommonPlugin;
import org.eclipse.emf.common.util.ResourceLocator;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.edit.ui.provider.DiagnosticDecorator;
import org.eclipse.osgi.util.NLS;
import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author Ed Merks
*/
@SuppressWarnings("nls")
public class NLSReport
{
private static final List<String> REPORT_HEAD = Arrays.asList(//
"<html>", //
"<head>", //
"<meta charset='utf-8'/>", //
"<title>NLS Report</title>", //
"<style>", //
"table, th, td {", //
" border: 1px solid black;", //
" border-collapse: collapse;", //
" white-space: pre;", //
"}", //
"th, td {", //
" text-align: left;", //
"}", //
"</style>", //
"</head>", //
"<body>", //
"<table>", //
"<tr>", //
"<th>", //
"Property", //
"</th>", //
"<th>", //
"Value", //
"</th>", //
"</tr>" //
);
private static final List<String> REPORT_TAIL = Arrays.asList(//
"<table>", //
"</body>", //
"</html>" //
);
private final File oomphGitClone;
private PrintStream out;
public NLSReport(File oomphGitClone)
{
this.oomphGitClone = oomphGitClone;
}
public File report()
{
openReport();
{
File pluginsFolder = new File(oomphGitClone, "plugins");
URI pluginsURI = URI.createFileURI(pluginsFolder.toString()).appendSegment("");
List<File> allFiles = IOUtil.listBreadthFirst(pluginsFolder);
Collections.sort(allFiles);
for (File file : allFiles)
{
URI fileURI = URI.createFileURI(file.toString());
URI relativeURI = fileURI.deresolve(pluginsURI);
String pluginID = relativeURI.segment(0);
String name = relativeURI.lastSegment();
if (name.equals("Messages.java"))
{
String className = relativeURI.toString().replaceAll("^.*src/", "").replaceAll("\\.java", "").replace('/', '.');
URI messagesURI = relativeURI.trimSegments(1).appendSegment("messages.properties");
reportMessages(pluginID, className, messagesURI);
}
else if (name.equals("MANIFEST.MF") && relativeURI.segmentCount() == 3 && "META-INF".equals(relativeURI.segment(1)))
{
Map<String, String> properties = PropertiesUtil.loadProperties(file);
String bundleActivator = properties.get("Bundle-Activator");
String bundleLocalization = properties.get("Bundle-Localization");
if (bundleLocalization != null)
{
File localizationFile = new File(pluginsFolder, pluginID + "/" + bundleLocalization + ".properties");
URI localizationFileURI = URI.createFileURI(localizationFile.toString()).deresolve(pluginsURI);
Map<String, String> localizationProperties = PropertiesUtil.loadProperties(localizationFile);
reportActivator(pluginID, bundleActivator, localizationFileURI, localizationProperties);
}
}
else if (name.equals("fragment.properties"))
{
Map<String, String> properties = PropertiesUtil.loadProperties(file);
report(relativeURI, properties);
}
}
}
{
File featuresFolder = new File(oomphGitClone, "features");
URI featuresURI = URI.createFileURI(featuresFolder.toString()).appendSegment("");
List<File> allFiles = IOUtil.listBreadthFirst(featuresFolder);
Collections.sort(allFiles);
for (File file : allFiles)
{
URI fileURI = URI.createFileURI(file.toString());
URI relativeURI = fileURI.deresolve(featuresURI);
String name = relativeURI.lastSegment();
if (name.equals("feature.properties") && relativeURI.segmentCount() == 2)
{
Map<String, String> properties = PropertiesUtil.loadProperties(file);
report(relativeURI, properties);
}
}
}
closeReport();
return reportFile;
}
private void openReport()
{
try
{
reportFile = File.createTempFile("NLSReport", ".html");
out = new PrintStream(reportFile, "UTF-8");
for (String line : REPORT_HEAD)
{
out.println(line);
}
}
catch (IOException ex)
{
throw new IORuntimeException(ex);
}
}
private void closeReport()
{
for (String line : REPORT_TAIL)
{
out.println(line);
}
out.close();
}
private static final List<String> SUBSTITUTIONS = Arrays.asList("{0}", "{1}", "{2}", "{3}", "{4}");
private void reportActivator(String pluginID, String bundleActivatorName, URI localizationFileURI, Map<String, String> localizationProperties)
{
for (Map.Entry<String, String> entry : localizationProperties.entrySet())
{
String value = entry.getValue();
int subsitutionCount = getSubsitutionCount(value);
if (subsitutionCount > 0)
{
Object[] substitutions = SUBSTITUTIONS.subList(0, subsitutionCount).toArray();
String bound1 = NLS.bind(value, substitutions);
String bound2 = NLS.bind(value, substitutions);
if (!bound1.equals(bound2) || !bound1.equals(value))
{
throw new RuntimeException();
}
}
}
if (bundleActivatorName != null)
{
try
{
Class<?> bundleActivatorClass = CommonPlugin.loadClass(pluginID, bundleActivatorName);
if (ResourceLocator.class.isAssignableFrom(bundleActivatorClass))
{
String containerClassName = bundleActivatorName.substring(0, bundleActivatorName.indexOf('$'));
Class<?> containerClass = CommonPlugin.loadClass(pluginID, containerClassName);
ResourceLocator plugin = ReflectUtil.getValue("plugin", containerClass);
for (Map.Entry<String, String> entry : localizationProperties.entrySet())
{
String value = plugin.getString(entry.getKey());
if (!value.equals(entry.getValue()))
{
throw new RuntimeException();
}
int subsitutionCount = getSubsitutionCount(value);
if (subsitutionCount > 0)
{
Object[] substitutions = SUBSTITUTIONS.subList(0, subsitutionCount).toArray();
String bound1 = NLS.bind(value, substitutions);
String bound2 = plugin.getString(entry.getKey(), substitutions);
if (!bound1.equals(bound2) || !bound1.equals(value))
{
throw new RuntimeException();
}
}
}
}
}
catch (Exception ex)
{
throw new RuntimeException(ex);
}
}
report(localizationFileURI, localizationProperties);
}
private void report(URI localizationFileURI, Map<String, String> localizationProperties)
{
out.println("<tr>");
out.print("<td colspan='2' style='font-weight: bold;'>");
out.print(DiagnosticDecorator.escapeContent(localizationFileURI.toString()));
out.println("</td>");
out.println("</tr>");
for (Map.Entry<String, String> entry : localizationProperties.entrySet())
{
out.println("<tr>");
out.print("<td>");
out.print(entry.getKey());
out.println("</td>");
out.print("<td>");
out.print(DiagnosticDecorator.escapeContent(entry.getValue()));
out.println("</td>");
out.println("</tr>");
}
}
private static final Pattern SUBSITUTION = Pattern.compile(" (?<!\\\\)\\{([0-9]+)\\}|^\\{([0-9]+)\\}");
private File reportFile;
private static int getSubsitutionCount(String message)
{
int count = 0;
for (Matcher matcher = SUBSITUTION.matcher(message); matcher.find();)
{
String index = matcher.group(1);
if (index == null)
{
index = matcher.group(2);
}
int value = Integer.parseInt(index);
if (value > count)
{
value = count;
}
}
return count;
}
private void reportMessages(String pluginID, String messagesClassName, URI messagesURI)
{
try
{
Class<?> messagesClass = CommonPlugin.loadClass(pluginID, messagesClassName);
Field[] fields = messagesClass.getFields();
Map<String, String> messages = new LinkedHashMap<String, String>();
for (Field field : fields)
{
Object value = field.get(null);
messages.put(field.getName(), value.toString());
}
report(messagesURI, messages);
}
catch (Exception ex)
{
throw new RuntimeException(ex);
}
}
}