/******************************************************************************* * Copyright (C) 2003-2008, 2013, Guillaume Brocker * Copyright (C) 2015-2016, Andre Bossert * * 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: * Guillaume Brocker - Initial API and implementation * Andre Bossert - added support of eclipse variables to resolve doxygen path * - added support of eclipse variables passed to environment * - Add ability to use Doxyfile not in project scope * - Refactoring of deprecated API usage * ******************************************************************************/ package eclox.core.doxygen; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.eclipse.core.resources.IFile; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.preferences.DefaultScope; import org.eclipse.core.runtime.preferences.IEclipsePreferences; import org.eclipse.core.runtime.preferences.IPreferencesService; import org.eclipse.core.runtime.preferences.InstanceScope; import org.eclipse.core.variables.IDynamicVariable; import org.eclipse.core.variables.IStringVariable; import org.eclipse.core.variables.IStringVariableManager; import org.eclipse.core.variables.IValueVariable; import org.eclipse.core.variables.VariablesPlugin; import eclox.core.IPreferences; import eclox.core.Plugin; /** * Implements the abstract doxygen frontend. Sub-classes provides concret * doxygen access. * * @author gbrocker */ public abstract class Doxygen { /** * a string containing defining the default doxygen command to use */ protected final static String COMMAND_NAME = "doxygen"; /** * Retrieves the default doxygen instance to use. */ public static Doxygen getDefault() { Doxygen doxygen = null; // get the actual default preference store //final String identifier = Plugin.getDefault().getPluginPreferences().getString( IPreferences.DEFAULT_DOXYGEN ); IPreferencesService service = Platform.getPreferencesService(); final String PLUGIN_ID = Plugin.getDefault().getBundle().getSymbolicName(); IEclipsePreferences defaultNode = DefaultScope.INSTANCE.getNode(PLUGIN_ID); IEclipsePreferences instanceNode = InstanceScope.INSTANCE.getNode(PLUGIN_ID); IEclipsePreferences[] nodes = new IEclipsePreferences[] {instanceNode, defaultNode}; final String identifier = service.get(IPreferences.DEFAULT_DOXYGEN, "", nodes); List<Class<? extends Doxygen>> doxygenClassList = new ArrayList<Class<? extends Doxygen>>(); doxygenClassList.add(DefaultDoxygen.class); doxygenClassList.add(CustomDoxygen.class); doxygenClassList.add(BundledDoxygen.class); for (Class<? extends Doxygen> doxygenClass : doxygenClassList) { doxygen = getFromClassAndIdentifier(doxygenClass, identifier); if (doxygen != null) break; } return doxygen; } public static Doxygen getFromClassAndIdentifier(Class<? extends Doxygen> doxygenClass, String identifier) { Doxygen doxygen = null; if( identifier.startsWith(doxygenClass.getName()) ) { String location = null; int locationIndex = identifier.indexOf('='); if (locationIndex == -1) locationIndex = identifier.indexOf(' '); if (locationIndex != -1) location = identifier.substring(locationIndex + 1 ); try { doxygen = doxygenClass.newInstance(); if (location != null) doxygen.setLocation(location); } catch (InstantiationException | IllegalAccessException e) { doxygen = null; } } return doxygen; } /** * Retrieves the version string of wrapped doxygen. * * @return a string containing the doxygen version string */ public String getVersion() { try { // create process builder with doxygen command ProcessBuilder pb = new ProcessBuilder(getCommand(), "--help"); // Runs the command and retrieves the version string. Process process = pb.start(); /*int ret = */process.waitFor(); BufferedReader input = new BufferedReader( new InputStreamReader(process.getInputStream()) ); BufferedReader error = new BufferedReader( new InputStreamReader(process.getErrorStream()) ); // Matches the doxygen welcome message. Pattern pattern = Pattern.compile( "^doxygen\\s+version\\s+([\\d\\.]+).*", Pattern.CASE_INSENSITIVE|Pattern.DOTALL ); Matcher matcher = null; String inputLine = input.readLine(); if (inputLine != null && inputLine.length() > 0) { matcher = pattern.matcher(inputLine); } if( matcher != null && matcher.matches() ) { return matcher.group( 1 ); } else { String errorMessage = new String(); String line; while( (line = error.readLine()) != null ) { errorMessage = errorMessage.concat(line); } throw new RuntimeException( "Unable to get doxygen version: " + errorMessage ); } } catch( Throwable t ) { Plugin.log( t ); return null; } } private String resolveOneVariable(String key, IStringVariableManager variableManager, boolean dynamicAllowed) { if (key != null) { if (variableManager == null) { variableManager = VariablesPlugin.getDefault() .getStringVariableManager(); } if (variableManager != null) { // static variable IValueVariable staticVar = variableManager.getValueVariable(key); if (staticVar != null) { return staticVar.getValue(); } // dynamic variable else if (dynamicAllowed) { String varName = key; String valuePar = null; // check if parameterized and get parameter int index = key.indexOf(':'); if (index > 1) { varName = key.substring(0, index); if (key.length() > index + 1) valuePar = key.substring(index + 1); } // get dynamic variable IDynamicVariable dynVar = variableManager .getDynamicVariable(varName); if (dynVar == null) return null; try { return dynVar.getValue(valuePar); } catch (CoreException e) { return null; } } } } return null; } private void resolveAllVariables(Map<String, String> varMap) { IStringVariableManager variableManager = VariablesPlugin.getDefault() .getStringVariableManager(); for (IStringVariable strVar : variableManager.getVariables()) { String name = strVar.getName(); if (name != null && !name.isEmpty()) { String value = resolveOneVariable(name, variableManager, false); if (value != null) { varMap.put(name, value); } } } } /** * Launch a documentation build. * * @param file the file representing the doxygen configuration to use for the build * * @return The process that run the build. */ public Process build( IFile file ) throws InvokeException, RunException { return build(file.getLocation().makeAbsolute().toFile()); } public Process build( File file ) throws InvokeException, RunException { if( file.exists() == false ) { throw new RunException("Missing or bad doxyfile"); } try { // create process builder with doxygen command and doxyfile ProcessBuilder pb = new ProcessBuilder(getCommand(), "-b", file.getAbsolutePath()); // set working directory and redirect error stream pb.directory(file.getParentFile().getAbsoluteFile()); pb.redirectErrorStream(true); // get passed system environment Map<String, String> env = pb.environment(); // add own variables, like GRAPHVIZ_PATH etc. //addEcloxVarsToEnvironment(env); // add all defined variables addAllVarsToEnvironment(env); // return the process return pb.start(); } catch(IOException ioException) { throw new InvokeException(ioException); } } private void addEcloxVarsToEnvironment(Map<String, String> env) { List<String> vars = new ArrayList<String>(); vars.add("GRAPHVIZ_PATH"); for (String name : vars) { if (name != null && !name.isEmpty()) { String value = resolveOneVariable(name, null, true); if (value != null) { env.put(name, value); } } } } private void addAllVarsToEnvironment(Map<String, String> env) { Map<String, String> resolvedVars = new HashMap<String, String>(); resolveAllVariables(resolvedVars); env.putAll(resolvedVars); } /** * Generate an empty configuration file. * * @param file the configuration file to generate. */ public void generate( IFile file ) throws InvokeException, RunException { try { Process process; // create process builder with doxygen command and doxyfile ProcessBuilder pb = new ProcessBuilder(getCommand(), "-g", file.getLocation().makeAbsolute().toOSString()); // Run the command and check for errors. process = pb.start(); if(process.waitFor() != 0) { BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); String errorMsg = new String(); String line; for(line=reader.readLine(); line != null; line=reader.readLine()) { errorMsg = errorMsg.concat(line); } throw new RunException( errorMsg ); } // Force some refresh to display the file. file.refreshLocal( 0, null ); } catch( RunException runException ) { throw runException; } catch( SecurityException securityException ) { throw new InvokeException(securityException); } catch( IOException ioException ) { throw new InvokeException(ioException); } catch( Throwable throwable ) { Plugin.log(throwable); } } /** * Updates the location of the custom doxygen. */ public abstract void setLocation(String location); /** * Retrieves the string containing the command line to the doxygen binary. * Sub-classes must implement this method. * * @return a string containing the path to the doxygen binary file */ public abstract String getCommand(); /** * Retrieves the description string of the doxygen wrapper instance. * * @return a string containing the description of the dowygen wrapper */ public abstract String getDescription(); /** * Retrieves the identifier of the doxygen wrapper. * * @return a string containing the doxygen wrapper identifier */ public abstract String getIdentifier(); }