/* * [SR.java] * * Summary: Prototype regex search replace. * * Copyright: (c) 2012-2017 Roedy Green, Canadian Mind Products, http://mindprod.com * * Licence: This software may be copied and used freely for any purpose but military. * http://mindprod.com/contact/nonmil.html * * Requires: JDK 1.8+ * * Created with: JetBrains IntelliJ IDEA IDE http://www.jetbrains.com/idea/ * * Version History: * 1.0 2010-10-31 initial version */ package com.mindprod.sr; import com.mindprod.commandline.CommandLine; import com.mindprod.common18.EIO; import com.mindprod.filter.AllButSVNDirectoriesFilter; import com.mindprod.filter.ExtensionListFilter; import com.mindprod.hunkio.HunkIO; import java.io.File; import java.io.FileNotFoundException; import java.io.FileWriter; import java.io.IOException; import java.util.regex.Matcher; import java.util.regex.Pattern; import static java.lang.System.*; /** * Prototype regex search replace. *

*

must hard code in list of regexes. Must put files on command line. * configure CONFIRM_LEVEL with hard coding * Each regex is applied on the whole file, one after the other on the output from the previous regex. * * @author Roedy Green, Canadian Mind Products * @version 1.0 2010-10-31 initial version * @since 2010-10-31 */ public class SR { /* 0 = search only, 1 = confirm, 2 = replace without confirmation */ private static final int CONFIRM_LEVEL = 1; /** * undisplayed copyright notice. * * @noinspection UnusedDeclaration */ private static final String EMBEDDED_COPYRIGHT = "Copyright: (c) 2012-2017 Roedy Green, Canadian Mind Products, http://mindprod.com"; /** * date this version released. * * @noinspection UnusedDeclaration */ private static final String RELEASE_DATE = "2012-10-31"; /** * how to use the command line */ private static final String USAGE = "\nSR needs a filename.html or a space-separated list of filenames, " + "with optional -s -q -v switches. The actual regexes are hard wired in for " + "now."; /** * embedded version string. * * @noinspection UnusedDeclaration */ private static final String VERSION_STRING = "1.0"; /** * replacement patterns, may contain $ and \ operators */ private static final String[] replacements = { "$1", "$1", "$1", "$1", "$1", "$1", "$1" }; /** * which regexes we search for */ private static final Pattern[] regexesToSearchFor = { Pattern.compile( ""(.+?)"", Pattern.DOTALL ), Pattern.compile( "“(.+?)”", Pattern.DOTALL ), Pattern.compile( "(.+?)", Pattern.DOTALL ), Pattern.compile( "(.+?)", Pattern.DOTALL ), Pattern.compile( "(.+?)", Pattern.DOTALL ), Pattern.compile( "(.+?)", Pattern.DOTALL ), Pattern.compile( "(.+?)", Pattern.DOTALL ) }; /** * constructor, not used. * * @noinspection WeakerAccess */ private SR() { } /** * apply one search/replace for one string containing file contents. * * @param contents string contain the entire contents of the file we are processing. * @param search array of Patterns to look for * @param replacement array of replacement strings. * @param fileBeingProcessed file we are processing, for messages onyl. */ private static String applyRegexToString( final String contents, final Pattern search, final String replacement, final File fileBeingProcessed ) { // where we collect the replacement. Must be a StringBuffer, not a StringBuilder. Regex has a legacy problem. // do not change to StringBuilder final StringBuffer sb = new StringBuffer( contents.length() * 2 ); final Matcher m = search.matcher( contents ); // Matchers are used both for matching and finding. while ( m.find() ) { out.println( ">>> found this in " + EIO.getCanOrAbsPath( fileBeingProcessed ) ); out.println( m.group( 0 ) ); // You don't HAVE to call appendReplacement for every match. If you don't, // that matched text will remain as is. // If replacement contains strings of the form $1 they are commands to include groups. // To turn this off, $ must be encoded as \$ // \ needs to be coded as \\ // The material between matches will also be automatically included, unchanged. // If you want to modify that text, appendReplacement is not the tool you want. switch ( CONFIRM_LEVEL ) { default: case 0: break; // just search case 1: // ask for confirmation try { for ( int available = System.in.available(); available > 0; available-- ) { // discard pending chars //noinspection ResultOfMethodCallIgnored System.in.read(); } out.println( " replace? y or n [enter]" ); char response = Character.toLowerCase( ( char ) in.read() ); if ( response == 'y' ) { m.appendReplacement( sb, replacement ); // no need for Matcher.quoteReplacement. out.println( " replaced" ); } else { out.println( " skipped" ); } } catch ( IOException e ) { err.println( e.getMessage() ); } break; case 2: // replace without confirm. m.appendReplacement( sb, replacement ); // no need for Matcher.quoteReplacement. break; } } m.appendTail( sb ); return sb.toString(); } /** * apply all search/replaces for one file. * * @param fileBeingProcessed the file currently being processed. */ private static void applyRegexesToFile( final File fileBeingProcessed ) throws IOException { final String original = HunkIO.readEntireFile( fileBeingProcessed ); String contents = original; // apply all the regexes to the file contents. assert regexesToSearchFor.length == replacements.length : "must have 1:1 search:replace"; for ( int i = 0; i < regexesToSearchFor.length; i++ ) { contents = applyRegexToString( contents, regexesToSearchFor[ i ], replacements[ i ], fileBeingProcessed ); } if ( contents.equals( original ) ) { // nothing changed. No need to write results. out.println( "- " + EIO.getCanOrAbsPath( fileBeingProcessed ) ); return; } out.println( "* " + EIO.getCanOrAbsPath( fileBeingProcessed ) ); final File tempFile = HunkIO.createTempFile( "temp_", ".tmp", fileBeingProcessed ); final FileWriter emit = new FileWriter( tempFile ); emit.write( contents ); emit.close(); // successfully created output in same directory as input, // Now make it replace the input file. HunkIO.deleteAndRename( tempFile, fileBeingProcessed ); } /** * FormatPadSites csv file to HTML, list of submission sites, either hassle or nohassle, or candidates. * Put name of file to edit on command line. * * @param args source and target file names. * * @throws java.io.IOException on trouble reading/writing files */ public static void main( String[] args ) throws IOException { // gather all the files mentioned on the command line. // either directories, files, with -s and subdirs option. // warning. Windows expands any wildcards in a nasty way. // do not use wildcards. // See http://mindprod.com/jgloss/wildcard.html out.println( "Gathering html files to Search/Replace..." ); CommandLine commandLine = new CommandLine( args, new AllButSVNDirectoriesFilter(), new ExtensionListFilter( ExtensionListFilter.COMMON_TEXT_EXTENSIONS ) ); if ( commandLine.size() == 0 ) { throw new IllegalArgumentException( "No files found to process\n" + USAGE ); } for ( File file : commandLine ) { try { applyRegexesToFile( file ); } catch ( FileNotFoundException e ) { out.println( "Error: " + EIO.getCanOrAbsPath( file ) + " not found." ); } catch ( IOException e ) { out.println( e.getMessage() + " in file " + EIO.getCanOrAbsPath( file ) ); } } // end for out.println( "done" ); } }