/*
* Name: PythonIndenter
* Author: Richard Rodger
*
* Copyright (c) 2001-2003 Richard Rodger
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published
* by the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
// package
package org.jostraca.section;
// import
import org.jostraca.Property;
import org.jostraca.PropertyPython;
import org.jostraca.util.Standard;
import org.jostraca.util.PropertySet;
import org.jostraca.util.RegExp;
import org.jostraca.util.RegExpException;
import java.util.Vector;
import java.util.StringTokenizer;
/** Indent insert statements properly.
* Note: tab support is flawed: either use tabs exclusively
* or use only spaces! In fact this class could really do with a complete
* rewrite! anyone?
*/
public class PythonIndenter extends Identity {
// public methods
/** Modify the Section.
* @param pSection Section to modify.
* @param pPropertySet PropertySet which may contain parameters for the modification.
*/
public Section modify( Section pSection, PropertySet pPropertySet ) throws ModifierException {
//System.out.println("PI.modify: pSection:"+pSection+" pPropertySet:\n"+pPropertySet);
Section check = checkNulls( pSection, pPropertySet );
if( null != check ) {
//System.out.println("PI.modify: null != check");
return check;
}
//if( "declare".equals( pSection.getName() ) ) {
// System.out.println("PI.modify: from: section:\n"+pSection.getContent());
//}
doInsertIndent( pSection, pPropertySet );
//if( "declare".equals( pSection.getName() ) ) {
// System.out.println("PI.modify: returning: section:\n"+pSection.getContent());
//}
return pSection;
}
// private methods
/** Prepend the indent of the section insert marker in the code writer format to
* the section text;
* @param pSection Section to indent.
* @param pPropertySet PropertySet which may contain parameters for the modification.
*/
private void doInsertIndent( Section pSection, PropertySet pPropertySet ) throws ModifierException {
//System.out.println("PI.doInsertIndent start");
String sectionName = pSection.getName();
String sectionText = pSection.getContent();
// perform indent
StringBuffer newSectionText = new StringBuffer( 48 * ( 1 + (sectionText.length() / 44) ) );
StringTokenizer st = new StringTokenizer( sectionText, Standard.NEWLINE );
boolean firstLine = true;
Vector veclines = new Vector();
while( st.hasMoreTokens() ) {
veclines.addElement( st.nextToken() );
}
String[] lines = (String[]) veclines.toArray( new String[] {} );
int numLines = lines.length;
String line = null;
String standardIndent = pPropertySet.get( PropertyPython.standardIndent );
String tabAsSpaces = pPropertySet.get( PropertyPython.tabAsSpaces );
String nonStatementLineRegExpDef = pPropertySet.get( PropertyPython.nonStatementLineRegExp );
String controlFlowRegExpDef = pPropertySet.get( PropertyPython.controlFlowRegExp );
String pythonInsertPrefix = pPropertySet.get( Property.lang_InsertPrefix );
// create regexps
RegExp nonStatementLineRegExp = null;
try {
nonStatementLineRegExp = RegExp.make( nonStatementLineRegExpDef );
} catch( RegExpException ree ) {
throw new ModifierException( "Unable to understand regular expression for matching non statement lines: "+
PropertyPython.nonStatementLineRegExp+"="+nonStatementLineRegExpDef
);
}
RegExp controlFlowRegExp = null;
try {
controlFlowRegExp = RegExp.make( controlFlowRegExpDef );
} catch( RegExpException ree ) {
throw new ModifierException( "Unable to understand regular expression for matching control flow lines: "+
PropertyPython.controlFlowRegExp+"="+controlFlowRegExpDef
);
}
//System.out.println("PI.doInsertIndent: numLines:" + numLines);
//System.out.println("PI.doInsertIndent: standardIndent:" + standardIndent);
//System.out.println("PI.doInsertIndent: tabAsSpaces:" + tabAsSpaces);
//System.out.println("PI.doInsertIndent: nonStatementLineRegExpDef:" + nonStatementLineRegExpDef);
//System.out.println("PI.doInsertIndent: controlFlowRegExpDef:" + controlFlowRegExpDef);
//System.out.println("PI.doInsertIndent: pythonInsertPrefix:" + pythonInsertPrefix);
try {
String prev = "";
String prevIndent = "";
boolean prevIsControlFlow = false;
String next = "";
String nextIndent = "";
next_line:
for( int lineI = 0; lineI < numLines; lineI++ ) {
//System.out.println("PI.doInsertIndent: lineI:"+lineI+" line: "+lines[lineI]);
if( -1 == lines[ lineI ].indexOf( pythonInsertPrefix ) ) {
newSectionText.append( lines[ lineI ] );
newSectionText.append( Standard.NEWLINE );
continue next_line;
}
//System.out.println("PI.doInsertIndent: _py_insert found");
// find indent of previous non-empty, non-comment line
prev = findStatement( lines, lineI, nonStatementLineRegExp, -1 );
prevIndent = findIndent( prev, tabAsSpaces );
if( null == prevIndent ) {
// no previous indent, so don't change line
newSectionText.append( lines[ lineI ] );
newSectionText.append( Standard.NEWLINE );
continue next_line;
}
line = lines[ lineI ];
// i.e. ends in :
prevIsControlFlow = isControlFlow( prev, controlFlowRegExp );
//System.out.println( "prev:"+prev+" iscf:"+prevIsControlFlow);
String newLine = Standard.EMPTY;
if( prevIsControlFlow ) {
next = findStatement( lines, lineI, nonStatementLineRegExp, +1 );
nextIndent = findIndent( next, tabAsSpaces );
//System.out.println( "next: "+next+" ni:["+nextIndent+"]");
if( null == nextIndent ) {
newLine = makeIndent( line, standardIndent );
//System.out.println( "1 :"+newLine );
}
else {
if( nextIndent.length() > prevIndent.length() ) {
// follow other statements in sub block
newLine = makeIndent( line, nextIndent );
//System.out.println( "2 :"+newLine );
} else {
// single statement in sub block
newLine = makeIndent( line, prevIndent + standardIndent );
//System.out.println( "3 :"+newLine );
}
}
}
else {
newLine = makeIndent( line, prevIndent );
}
//System.out.println( "l :"+line );
//System.out.println( "nl:"+newLine );
lines[ lineI ] = newLine;
newSectionText.append( newLine );
newSectionText.append( Standard.NEWLINE );
}
if( Standard.NEWLINE.length() <= newSectionText.length() ) {
if( !sectionText.endsWith( Standard.NEWLINE ) ) {
pSection.setContent( newSectionText.substring(0, newSectionText.length() - Standard.NEWLINE.length() ) );
} else {
pSection.setContent( newSectionText.toString() );
}
} else {
pSection.setContent( newSectionText.toString() );
}
} catch( RegExpException ree ) {
throw new ModifierException( ree );
}
}
private String findStatement( String[] pLines
,int pStartLineIndex
,RegExp pNonStatementRegExp
,int pSearchDirection
) throws RegExpException {
//System.out.println("PI.findStatement: sl:"+pStartLineIndex+" sd:"+pSearchDirection);
String result = null;
int lineI = pStartLineIndex + pSearchDirection;
int numLines = pLines.length;
while( -1 < lineI
&& lineI < numLines
&& ( !Standard.EMPTY.equals( pNonStatementRegExp.match( pLines[lineI] ) ) )
) {
lineI += pSearchDirection;
}
if( -1 < lineI
&& lineI < numLines
) {
result = pLines[ lineI ];
}
//System.out.println("PI.findStatement: result:"+result);
return result;
}
/** Return the indent of the line, replacing any tabs with pTabAsSpaces
*/
private String findIndent( String pLine, String pTabAsSpaces ) {
//System.out.println("PI.findIndent: pTabAsSpaces:#"+pTabAsSpaces+"# pLine:"+pLine);
if( null == pLine ) {
//System.out.println("PI.findIndent: null == pLine, returning null");
return null;
}
String result = null;
boolean stop = false;
StringBuffer indent = new StringBuffer(8);
int numChars = pLine.length();
for( int charI = 0; charI < numChars; charI++ ) {
switch( pLine.charAt( charI ) ) {
case ' ':
indent.append(' ');
break;
case '\t':
indent.append( pTabAsSpaces );
break;
default:
stop = true;
break;
}
if( stop ) {
break;
}
}
result = indent.toString();
//System.out.println("PI.findIndent: result: #"+result+"#");
return result;
}
/** True if statement ends with a :
* Deals with comments
*/
private boolean isControlFlow( String pLine, RegExp pControlFlowRegExp ) throws RegExpException {
boolean result = ( !Standard.EMPTY.equals( pControlFlowRegExp.match( pLine ) ) );
//System.out.println("PI.isControlFlow: result:"+result+" line:"+pLine);
return result;
}
/** Set the indent of the given line.
*/
private String makeIndent( String pLine, String pIndent ) {
//System.out.println("PI.makeIndent: pIndent:#"+pIndent+"# pLine:"+pLine);
if( null == pLine ) {
//System.out.println("PI.makeIndent: null == pLine, returning null");
return null;
}
String result = null;
boolean stop = false;
int numChars = pLine.length();
int charI = 0;
for( charI = 0; charI < numChars; charI++ ) {
switch( pLine.charAt( charI ) ) {
case ' ':
break;
case '\t':
break;
default:
stop = true;
break;
}
if( stop ) {
break;
}
}
result = pIndent + pLine.substring( charI );
//System.out.println("PI.makeIndent: result:"+result);
return result;
}
}