/******************************************************************************* * Copyright (C) 2003-2004, 2008, 2013, Guillaume Brocker * * 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 * ******************************************************************************/ package eclox.core.doxyfiles.io; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.regex.Matcher; import java.util.regex.Pattern; //import eclox.core.Services; import eclox.core.Plugin; import eclox.core.doxyfiles.Chunk; import eclox.core.doxyfiles.Doxyfile; import eclox.core.doxyfiles.RawText; import eclox.core.doxyfiles.Setting; /** * Implements a doxyfile parser. * * @author gbrocker */ public class Parser { /** * the current line number */ private int lineNumber = 0; /** * The line reader used to parse the input stream. */ private BufferedReader reader; /** * The comment line pattern. */ private Pattern commentPattern = Pattern.compile("#.*"); /** * the empty line pattern */ private Pattern emptyPattern = Pattern.compile("\\s*"); /** * the setting assignment pattern */ private Pattern settingAssignmentPattern = Pattern.compile("(\\w+)\\s*=\\s*(.*?)\\s*(\\\\)?"); /** * the setting increment pattern */ private Pattern settingIncrementPattern = Pattern.compile("(\\w+)\\s*\\+=\\s*(.*?)\\s*(\\\\)?"); /** * the continued setting assignment pattern */ private Pattern continuedSettingAssignmentPattern = Pattern.compile("\\s*(.+?)\\s*(\\\\)?"); /** * the include directive pattern */ private Pattern includePattern = Pattern.compile("@INCLUDE\\s*=\\s*(.*)"); /** * the include path pattern */ private Pattern includePathPattern = Pattern.compile("@INCLUDE_PATH\\s*=\\s*(.*)"); /** * Constructor. * * @param input an input stream instance to parse as a doxyfile */ public Parser( InputStream input ) throws IOException { this.reader = new BufferedReader( new InputStreamReader(input) ); this.reader.mark(0); } /** * Reads the input stream and returns the doxyfile. * * @param doxyfile a doxyfile where the parser results will be stored * * @return a collection of the setting read from the input stream */ public void read( Doxyfile doxyfile ) throws IOException { // Initialization of the system. this.reader.reset(); this.lineNumber = 0; // Reads and parses all lines. try { String line; for( line = reader.readLine(); line != null; line = reader.readLine() ) { lineNumber++; this.matchLine( doxyfile, line ); } } catch(Throwable throwable) { throw new IOException( "Syntax error at line " + lineNumber + ". " + throwable.getMessage() ); } } /** * Matches the specified line. * * @param doxyfile a doxyfile where the result will be stored * @param line a string containing the current line text */ private void matchLine( Doxyfile doxyfile, String line ) throws IOException { Matcher matcher; // Matches the current line against an empty line pattern matcher = emptyPattern.matcher( line ); if( matcher.matches() == true ) { this.processAnyLine( doxyfile, line ); return; } // Matches the current line against the comment pattern. matcher = commentPattern.matcher( line ); if( matcher.matches() == true ) { this.processAnyLine( doxyfile, line ); return; } // Matches the current line against the setting assignment pattern. matcher = settingAssignmentPattern.matcher( line ); if( matcher.matches() == true ) { // Retrieves the setting identifier and its values. String identifier = matcher.group(1); String values = matcher.group(2); // Call the traitement for the setting assignment and pull out. this.processSettingAssignment( doxyfile, identifier, values ); return; } // Matches the current line against the setting increment pattern. matcher = settingIncrementPattern.matcher( line ); if( matcher.matches() == true ) { // Retrieves the setting identifier and its values. String identifier = matcher.group(1); String values = matcher.group(2); // Call the treatment for the setting assignment and pull out. this.processSettingIncrement( doxyfile, identifier, values ); return; } // Matches the current line against the include directive pattern. matcher = includePattern.matcher( line ); if( matcher.matches() == true ) { this.processAnyLine( doxyfile, line ); return; } // Matches the current line agains the include path directive pattern. matcher = includePathPattern.matcher( line ); if( matcher.matches() == true ) { this.processAnyLine( doxyfile, line ); return; } // Matches the current line against the continued setting assignment pattern. matcher = continuedSettingAssignmentPattern.matcher( line ); if( matcher.matches() == true ) { // Retrieves the setting identifier and its values. String values = matcher.group(1); String continued = matcher.group(2); // Call the treatment for the continued setting assignment and pull out. this.processContinuedSettingAssignment( doxyfile, values, continued != null ); return; } // The line has not been recognized. throw new IOException( "Unable to match line." ); } /** * Processes any line of a doxyfile that is not interesting and should only be stored * for later use (saving for example). * * @param doxyfile a doxyfile where the line will be stored * @param text a string containing the line text */ private void processAnyLine( Doxyfile doxyfile, String text ) { // Retrieves the last raw text chunk. Chunk lastChunk = doxyfile.getLastChunk(); RawText rawText; if( lastChunk instanceof RawText ) { rawText = (RawText) lastChunk; } else { rawText = new RawText(); doxyfile.append( rawText ); } // Stores the line's text in the raw text chunk. rawText.append( text ); rawText.append( "\n" ); } /** * Processes a setting assignment line. * * @param doxyfile a doxyfile where the setting assignment will be stored * @param identifier a string containing the setting identifier * @param value a string containing the assigned value */ private void processSettingAssignment( Doxyfile doxyfile, String identifier, String value ) throws IOException { // Retrieves the setting. Setting setting = doxyfile.getSetting( identifier ); if( setting == null ) { setting = new Setting( identifier, value ); doxyfile.append( setting ); } // Updates the setting value. setting.setValue( value ); } /** * Processes a setting increment line. * * @param doxyfile a doxyfile where the setting assignment will be stored * @param identifier a string containing the setting identifier * @param value a string containing the assigned value */ private void processSettingIncrement( Doxyfile doxyfile, String identifier, String value ) throws IOException { // Retrieves the setting from the doxyfile. Setting setting = doxyfile.getSetting( identifier ); if( setting != null ) { // Updates the continued setting's value. setting.setValue( setting.getValue() + " " + value ); } else { Plugin.getDefault().logWarning( "At line " + lineNumber + ": the setting was not declared before." ); processSettingAssignment( doxyfile, identifier, value ); } } /** * Processes a setting assignment line. * * @param doxyfile a doxyfile where the setting assignment will be stored * @param value a string containing the assigned value * @param continued a boolean telling if the setting assignment is continued on multiple line */ private void processContinuedSettingAssignment( Doxyfile doxyfile, String value, boolean continued ) throws IOException { Chunk lastChunk = doxyfile.getLastChunk(); // Ensures that a continued setting has been remembered // and updates it. if( lastChunk instanceof Setting ) { Setting continuedSetting = (Setting) lastChunk; continuedSetting.setValue( continuedSetting.getValue() + " " + value ); } else { Plugin.getDefault().logWarning( "At line " + lineNumber + ": value delcared without a setting name." ); } } }