blob: f3e3fbac614b76df7c649c2d9753dcc23c9b17e5 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010 BSI Business Systems Integration AG.
* 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:
* BSI Business Systems Integration AG - initial API and implementation
******************************************************************************/
package org.eclipse.scout.rt.spec.client;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.scout.commons.IOUtility;
import org.eclipse.scout.commons.exception.ProcessingException;
import org.eclipse.scout.commons.logger.IScoutLogger;
import org.eclipse.scout.commons.logger.ScoutLogManager;
import org.eclipse.scout.rt.spec.client.SpecIOUtility.IStringProcessor;
import org.eclipse.scout.rt.spec.client.config.SpecFileConfig;
import org.eclipse.scout.rt.spec.client.gen.DocGenUtility;
import org.osgi.framework.Bundle;
/**
* A post processor for generating linear output files (mediawiki and html)
* <p>
* The generated output files will have the same base filename as the configuration file with differnt ending.
*/
public class LinearOutputPostProcessor implements ISpecProcessor {
private static IScoutLogger LOG = ScoutLogManager.getLogger(LinearOutputPostProcessor.class);
public static final String ANCHOR_PREFIX = "lo_";
private File m_configFile;
private String m_outputFileName;
/**
* @param configFile
* Filename of configuration file which will be searched in {@link SpecFileConfig#getRelativeSourceDirPath()}
* in all {@link SpecFileConfig#getSourceBundles()} according their priority.
* <p>
* The configuration file is expected to have the file ending <code>.config</code>
* @throws ProcessingException
*/
public LinearOutputPostProcessor(String configFile) throws ProcessingException {
List<Bundle> sourceBundles = SpecIOUtility.getSpecFileConfigInstance().getSourceBundles();
Collections.reverse(sourceBundles);
for (Bundle bundle : sourceBundles) {
List<String> fileList = SpecIOUtility.listFiles(bundle, SpecIOUtility.getSpecFileConfigInstance().getRelativeSourceDirPath(), getConfigFileFilter(configFile));
if (fileList.size() > 0) {
m_configFile = new File(SpecIOUtility.getSpecFileConfigInstance().getSpecDir(), configFile);
SpecIOUtility.copyFile(bundle, SpecIOUtility.getSpecFileConfigInstance().getRelativeSourceDirPath() + File.separator + configFile, m_configFile);
break;
}
}
if (m_configFile == null || !m_configFile.exists()) {
LOG.error("Configfile " + configFile + " could not be copied!");
}
else {
m_outputFileName = m_configFile.getName().replace(".config", ".mediawiki");
}
}
protected FilenameFilter getConfigFileFilter(final String configFile) {
return new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
return name.equals(configFile);
}
};
}
@Override
public void process() throws ProcessingException {
if (m_configFile == null || !m_configFile.exists()) {
return;
}
File outputFile = concatenateFiles();
prefixAnchorsAndLinks(outputFile);
}
protected void prefixAnchorsAndLinks(File outputFile) throws ProcessingException {
SpecIOUtility.process(outputFile, new P_AnchorProcessor("(\\{\\{a:)([^}]+}})"));
SpecIOUtility.process(outputFile, new P_AnchorProcessor("(\\[\\[)([A-Za-z][A-Za-z0-9_\\.-]+\\|)"));
}
protected static class P_AnchorProcessor implements IStringProcessor {
private String m_twoGroupRegex;
/**
* @param string
*/
public P_AnchorProcessor(String twoGroupRegex) {
m_twoGroupRegex = twoGroupRegex;
}
@Override
public String processLine(String input) {
Pattern pattern = Pattern.compile(m_twoGroupRegex);
Matcher matcher = pattern.matcher(input);
StringBuilder sb = new StringBuilder();
int index = 0;
while (matcher.find()) {
sb.append(input.substring(index, matcher.start(0)));
sb.append(matcher.group(1)).append(ANCHOR_PREFIX).append(matcher.group(2));
index = matcher.end();
}
sb.append(input.substring(index));
return sb.toString();
}
}
protected File concatenateFiles() throws ProcessingException {
File outputFile = null;
List<String> configEntries = IOUtility.readLines(m_configFile, "UTF-8");
List<String> referencedFileNames = new ArrayList<String>();
PrintWriter writer = null;
outputFile = new File(SpecIOUtility.getSpecFileConfigInstance().getMediawikiDir(), m_outputFileName);
outputFile.delete();
try {
outputFile.createNewFile();
writer = new PrintWriter(outputFile);
for (String configEntry : configEntries) {
if (!isComment(configEntry)) {
File file = findFileForConfigEntry(configEntry);
if (file != null) {
referencedFileNames.add(file.getName());
IOUtility.appendFile(writer, file);
writer.flush();
}
else {
LOG.warn("No file found for config entry: " + configEntry + ". Skipping...");
}
}
}
}
catch (IOException e) {
throw new ProcessingException("Error creating output file.", e);
}
finally {
if (writer != null) {
writer.close();
}
}
logMissingEntries(referencedFileNames);
return outputFile;
}
/**
* log all mediawiki files that are not referenced in config
*
* @param referencedFilenames
* @throws ProcessingException
*/
protected void logMissingEntries(List<String> referencedFilenames) throws ProcessingException {
final String suffix = ".mediawiki";
String[] fileList = SpecIOUtility.getSpecFileConfigInstance().getMediawikiDir().list(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
return name.endsWith(suffix);
}
});
if (fileList != null) {
for (String file : fileList) {
if (!referencedFilenames.contains(file) && !m_outputFileName.equals(file)) {
LOG.warn("There is no entry for the existing file " + file + " in " + m_configFile.getName() + ".");
}
}
}
}
protected boolean isComment(String configEntry) {
return configEntry.startsWith("#");
}
protected File findFileForConfigEntry(String configEntry) throws ProcessingException {
// priority 1: try if entry is a file base name
File file = new File(SpecIOUtility.getSpecFileConfigInstance().getMediawikiDir(), configEntry + ".mediawiki");
if (file.exists()) {
return file;
}
// priority 2: try doc entity class name
Set<Class<?>> allClasses = DocGenUtility.getAllDocEntityClasses();
for (Class<?> c : allClasses) {
if (configEntry.equals(c.getSimpleName()) || configEntry.equals(c.getName())) {
File fileForClass = findFileForClass(c);
if (fileForClass == null) {
Set<Class> subclasses = getSubClassesOrderdByDistance(c, allClasses);
for (Class subclass : subclasses) {
fileForClass = findFileForClass(subclass);
if (fileForClass != null) {
break;
}
}
}
return fileForClass;
}
}
return null;
}
protected Set<Class> getSubClassesOrderdByDistance(final Class<?> c, Set<Class<?>> allClasses) {
TreeSet<Class> subclasses = new TreeSet<Class>(new Comparator<Class>() {
@Override
public int compare(Class o1, Class o2) {
Integer distanceO1 = getDistance(o1, c);
Integer distanceO2 = getDistance(o2, c);
return distanceO1.compareTo(distanceO2);
}
});
for (Class candidate : allClasses) {
if (c.isAssignableFrom(candidate) && c != candidate) {
subclasses.add(candidate);
}
}
return subclasses;
}
/**
* evaluates the distance in the class hierarchy between a sub class and a super class
*
* @param subClass
* @param superClass
* @return
* @throws IllegalArgumentException
* if <code>subClass</code> is not a real sub class of <code>superClass</code> (In particular this is the
* case if one of them is an interface type or they are equal.)
*/
protected static int getDistance(Class subClass, Class<?> superClass) {
if (!superClass.isAssignableFrom(subClass) || subClass == superClass || subClass.isInterface() || superClass.isInterface()) {
throw new IllegalArgumentException();
}
int distance = 0;
Class c = subClass;
while (c != superClass) {
c = c.getSuperclass();
++distance;
}
return distance;
}
protected File findFileForClass(Class c) throws ProcessingException {
File[] mediawikiFiles = SpecIOUtility.getSpecFileConfigInstance().getMediawikiDir().listFiles();
for (File file : mediawikiFiles) {
if (file.getName().startsWith(c.getSimpleName() + "_")) {
return file;
}
}
return null;
}
}