Tuesday, January 10, 2012

Deleting all JS comments from JSP page at build time: Part 2

Writing an ANT task is easy; all you need to extend is Task Class.  I have written one, it is simple java program.  This ant task is now being added in our application build tasks. This will help us in keeping the developers comments for readability and future reference, but cleaning all the JS comments from final build, hence mitigating the reputation risk. Here is the ant task declaration and configuration.

<taskdef name="jsscript" classname="com.aman.task.JSPDeleteScriptCommensRegExp"/>

<target name="allcomments">
<fileset id="jsp.fileset" dir="${src.dir}" includes="**/*.jsp"/>

<jsscript matchInline="(^|[^:\-'\&quot;])(//+.*(?!(\*/))$)" replaceInline="\1" matchMultiline="(^|[^/])(/\*+([\S\s](?!(/+\*+)))+?\*+/+)" replaceMultiline="\1">
            <fileset refid="jsp.fileset"/>
      </jsscript>
</target>

com.aman.task.JSPDeleteScriptCommensRegExp is the class that I have written to delete all the JS comment.

matchInlineis the property of the above class, this hold the inline comment pattern and this will be replaced with replaceInline  “\1”: this is the backreference of regular expression. We are replacing only the first group as second group is all comment.

matchMultilineis the again a similar property to delete multiline comments and this will be replaced by replaceMultiline – “\1”.

Matching and replacing the inline and multiline comments are handled differently.
Every line is searched for and replaced with pattern one at a time in inline matching, whereas in multiline all the code inside the script block is first buffred in and then matched and replaced.

I am not describing the regex used in detail, but if you have any concern please do contact me.

Here is the full code for reference

package com.aman.task;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.Writer;
import java.util.Vector;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.taskdefs.optional.ReplaceRegExp;
import org.apache.tools.ant.types.FileSet;
import org.apache.tools.ant.types.RegularExpression;
import org.apache.tools.ant.types.Substitution;
import org.apache.tools.ant.util.FileUtils;
import org.apache.tools.ant.util.regexp.Regexp;


public class JSPDeleteScriptCommensRegExp extends Task {

       private long totalline;
       private long comment;
       private long mcomment;
       private Vector filesets;
       private RegularExpression inlineRegex;
       private Substitution inlineSubs;
       private RegularExpression multilineRegex;
       private Substitution multiLineSubs;
       FileUtils fileUtils = FileUtils.getFileUtils();
     
       public JSPDeleteScriptCommensRegExp() {
                     super();
                     this.filesets = new Vector();
                    
                         this.inlineRegex = null;
                     this.inlineSubs = null;
       }
       
       public void setMatchInline(String match) {
                    if (inlineRegex != null) {
                         throw new BuildException("Only one inline regular expression is allowed");
                     }
                             inlineRegex = new RegularExpression();
                         inlineRegex.setPattern(match);
       }
       
       public void setReplaceInline(String replace) {
                if (inlineSubs != null) {
                         throw new BuildException("Only one inline substitution expression is "
                                                + "allowed");
                   }
     
              inlineSubs = new Substitution();
              inlineSubs.setExpression(replace);
      }
     
       public void setMatchMultiline(String match) {
              if (multilineRegex != null) {
                   throw new BuildException("Only one multiline regular expression is allowed");
               }
              multilineRegex = new RegularExpression();
              multilineRegex.setPattern(match);
      }
     
      public void setReplaceMultiline(String replace) {
             if (multiLineSubs != null) {
                        throw new BuildException("Only one multiline substitution expression is "
                                                + "allowed");
                   }
     
             multiLineSubs = new Substitution();
             multiLineSubs.setExpression(replace);
      }
       
       public void addFileset(FileSet set) {
             filesets.addElement(set);
     }
       
       protected String doReplace(RegularExpression r, Substitution s, String input, int options) {
                      String res = input;
                     Regexp regexp = r.getRegexp(getProject());
                 
                   if (regexp.matches(input, options)) {
                       log("Found match; substituting", Project.MSG_DEBUG);
                       res = regexp.substitute(input, s.getExpression(getProject()),
                                               options);
                   }
           
                    return res;
         }
       
       protected void doReplaceInline(File f, int options) throws IOException {
           
            File temp = fileUtils.createTempFile("replace", ".txt", null);
        temp.deleteOnExit();

        Reader r = null;
        Writer w = null;

        try {
          
            r = new FileReader(f);
            w = new FileWriter(temp);
          
            BufferedReader br = new BufferedReader(r);
            BufferedWriter bw = new BufferedWriter(w);
            PrintWriter pw = new PrintWriter(bw);

            boolean changes = false;

           
            StringBuffer linebuf = new StringBuffer();
            String line = null;
            String res = null;
            int c;
            boolean scriptFound = false;
            Pattern scriptStart = Pattern.compile("<script[\\S\\s]*?>");
            Pattern inlineScript = Pattern.compile("<script[\\S\\s]*?((/>)|(</script>))");
            Pattern scriptend = Pattern.compile("</script");
            while((line = br.readLine()) != null)
            {
                  totalline++;
                  Matcher scriptMacther = scriptStart.matcher(line);
                  Matcher inlineMatcher = inlineScript.matcher(line);
                  Matcher sciptendMatcher = scriptend.matcher(line);
                  if(inlineMatcher.find())
                  {
                        // dont do anything just print it in final file and escape
                  }
                  else if(scriptMacther.find())
                  {
                        scriptFound = true;
                       
                  }
                  else if(sciptendMatcher.find())
                  {
                        scriptFound = false;
                       
                  }
                 
                  if(scriptFound)
                  {
                        res  = doReplace(inlineRegex, inlineSubs, line, options);
                        if (!res.equals(line)) {
                        changes = true;
                        comment++;
                    }

                    pw.println(res);
                  }
                  else
                  {
                        pw.println(line);
                  }
            }
            pw.flush();
            r.close();
            r = null;
            w.close();
            w = null;
            if (changes) {
                log("File has changed; saving the updated file", Project.MSG_VERBOSE);
                try {
                    fileUtils.rename(temp, f);
                    temp = null;
                } catch (IOException e) {
                    throw new BuildException("Couldn't rename temporary file "
                                             + temp, getLocation());
                }
            } else {
                log("No change made", Project.MSG_DEBUG);
            }
        } finally {
            try {
                if (r != null) {
                    r.close();
                }
            } catch (Exception e) {
                // ignore any secondary exceptions
            }

            try {
                if (w != null) {
                    w.close();
                }
            } catch (Exception e) {
                // ignore any secondary exceptions
            }
            if (temp != null) {
                temp.delete();
            }
        }  
      }
       
       protected void doReplaceMuliline(File f, int options) throws IOException {
                 
               File temp = fileUtils.createTempFile("replace", ".txt", null);
             temp.deleteOnExit();

             Reader r = null;
             Writer w = null;

             try {
               
                 r = new FileReader(f);
                 w = new FileWriter(temp);
               
                 BufferedReader br = new BufferedReader(r);
                 BufferedWriter bw = new BufferedWriter(w);
                 PrintWriter pw = new PrintWriter(bw);

                 boolean changes = false;

                 StringBuffer linebuf = new StringBuffer();
                 String line = null;
                 String res = null;
                 int c;
                 boolean scriptFound = false;
                 boolean startMatch = false;
                 Pattern scriptStart = Pattern.compile("<script[\\S\\s]*?>");
                 Pattern inlineScript = Pattern.compile("<script[\\S\\s]*?((/>)|(</script>))");
                 Pattern scriptend = Pattern.compile("</script");
                 while((line = br.readLine()) != null)
                 {
                 Matcher scriptMacther = scriptStart.matcher(line);
                 Matcher inlineMatcher = inlineScript.matcher(line);
                 Matcher sciptendMatcher = scriptend.matcher(line);
                 if(inlineMatcher.find())
                 {
                 }
                 else if(scriptMacther.find())
                 {
                        scriptFound = true;
                 }
                 else if(sciptendMatcher.find() && scriptFound)
                 {
                        startMatch = true;
                        scriptFound = false;
                 }
                
                 if(scriptFound || (!scriptFound && startMatch))
                 {
                        linebuf.append(line).append(System.getProperty("line.separator"));
                 }
                 else
                 {
                        pw.println(line);
                 }
                
                 if(startMatch)
                 {
                        line = linebuf.toString();
                        res  = doReplace(multilineRegex, multiLineSubs, line, options);
                        if (!res.equals(line)) {
                         changes = true;
                         mcomment++;
                        }

                     pw.print(res);
                     linebuf = new StringBuffer();
                     startMatch = false;
                 }
                 }
                 pw.flush();
                 r.close();
                 r = null;
                 w.close();
                 w = null;
                 if (changes) {
                     log("File has changed; saving the updated file", Project.MSG_VERBOSE);
                     try {
                         fileUtils.rename(temp, f);
                         temp = null;
                     } catch (IOException e) {
                         throw new BuildException("Couldn't rename temporary file "
                                                  + temp, getLocation());
                     }
                 } else {
                     log("No change made", Project.MSG_DEBUG);
                 }
             } finally {
                 try {
                     if (r != null) {
                         r.close();
                     }
                 } catch (Exception e) {
                     // ignore any secondary exceptions
                 }

                 try {
                     if (w != null) {
                         w.close();
                     }
                 } catch (Exception e) {
                     // ignore any secondary exceptions
                 }
                 if (temp != null) {
                     temp.delete();
                 }
             }  
            }


      public void execute() throws BuildException {
            if (inlineRegex == null) {
                  throw new BuildException("No inline expression to match.");
            }
            if (inlineSubs == null) {
                  throw new BuildException("Nothing to replace inline expression with.");
            }
           
            if (multilineRegex == null) {
                  throw new BuildException("No multiline expression to match.");
            }
            if (multiLineSubs == null) {
                  throw new BuildException("Nothing to replace multiline expression with.");
            }

           
            int options = 0;

           
            options |= Regexp.REPLACE_ALL;
        options |= Regexp.MATCH_CASE_INSENSITIVE;
   
        int sz = filesets.size();

       for (int i = 0; i < sz; i++) {
        FileSet fs = (FileSet) (filesets.elementAt(i));
        DirectoryScanner ds = fs.getDirectoryScanner(getProject());

        String[] files = ds.getIncludedFiles();

        for (int j = 0; j < files.length; j++) {
            File f = new File(fs.getDir(getProject()), files[j]);

            if (f.exists()) {
                try {
                  long tempTotalLine =totalline;
                  long tempComment = comment;
                  long tempMComment = mcomment;
                  String fileNameWithPath = f.getAbsolutePath();
                    doReplaceInline(f, options);
                    doReplaceMuliline(f, options);
                    log("File - "+fileNameWithPath +" Line - "+(totalline-tempTotalLine)+"            inline comments - "+(comment-tempComment)+"           multiline comments - "+(mcomment-tempMComment), Project.MSG_INFO);
                } catch (Exception e) {
                    log("An error occurred processing file: '"
                        + f.getAbsolutePath() + "': " + e.toString(),
                        Project.MSG_ERR);
                }
            } else {
                log("The following file is missing: '"
                    + f.getAbsolutePath() + "'", Project.MSG_ERR);
                  }
            }
      }
       log("Total Line - "+totalline+"          total inline comments - "+comment+"       total multiline comments - "+mcomment);
      }
     
     
}


No comments:

Post a Comment