001 package hirondelle.web4jtools.metrics.tabs; 002 003 import static hirondelle.web4j.util.Consts.EMPTY_STRING; 004 import static hirondelle.web4j.util.Consts.NEW_LINE; 005 import static hirondelle.web4j.util.Consts.SPACE; 006 import hirondelle.web4j.action.ResponsePage; 007 import hirondelle.web4j.request.RequestParser; 008 import hirondelle.web4j.util.Util; 009 import hirondelle.web4jtools.metrics.base.FileInfo; 010 import hirondelle.web4jtools.metrics.base.MetricsAction; 011 import hirondelle.web4jtools.util.Ensure; 012 013 import java.io.BufferedWriter; 014 import java.io.File; 015 import java.io.FileNotFoundException; 016 import java.io.FileOutputStream; 017 import java.io.IOException; 018 import java.io.OutputStreamWriter; 019 import java.io.Writer; 020 import java.util.Iterator; 021 import java.util.Scanner; 022 import java.util.logging.Logger; 023 024 import javax.servlet.ServletConfig; 025 026 /** 027 * Replace all tab characters in source code with spaces. 028 * The number of spaces used is controlled by the 029 * <tt>NumSpacesForTabs</tt> setting in <tt>web.xml</tt>. 030 * 031 * <P>Since this action can potentially make a large number of <em>irreversible</em> 032 * edits to your source code, it is prudent to make a back up copy of your source before 033 * executing. 034 */ 035 public class TabsRemoveAction extends MetricsAction { 036 037 /** Read in config from web.xml. Called only upon startup. */ 038 public static void readConfig(ServletConfig aConfig){ 039 fNUM_SPACES_FOR_TABS = aConfig.getInitParameter(NUM_SPACES_FOR_TABS); 040 Ensure.isPresentInWebXml(NUM_SPACES_FOR_TABS, fNUM_SPACES_FOR_TABS); 041 if( Integer.valueOf(fNUM_SPACES_FOR_TABS) < 1 ) { 042 throw new IllegalArgumentException(NUM_SPACES_FOR_TABS + " setting in web.xml must be 1 or more. Value : " + Util.quote(fNUM_SPACES_FOR_TABS)); 043 } 044 fREPLACEMENT_FOR_TAB = getReplacementForTabs(); 045 fENCODING = aConfig.getInitParameter(ENCODING); 046 Ensure.isPresentInWebXml(ENCODING, fENCODING); 047 } 048 049 /** Full constructor. */ 050 public TabsRemoveAction(RequestParser aRequestParser) { 051 super(FORWARD, aRequestParser); 052 } 053 054 /** 055 * For all source code files, replace tab characters with spaces. 056 */ 057 @Override protected void calculateMetric() { 058 int tabsCount = 0; 059 Iterator<FileInfo> iter = fFileList.iterator(); 060 while ( iter.hasNext() ){ 061 FileInfo file = iter.next(); 062 if ( file.getNumTabs() > 0 ) { 063 fLogger.fine("Replacing tabs in " + file.getName().getRawString()); 064 tabsCount = tabsCount + replaceTabsAndUpdateFileInfo(file); 065 } 066 } 067 fLogger.fine("Total number of tab characters replaced in source files: " + tabsCount); 068 addMessage("Please re-scan your source code (Summary page) to verify no TAB chars remain."); 069 setResponsePage(REDIRECT_TO_LISTING); 070 } 071 072 // PRIVATE // 073 private static final String NUM_SPACES_FOR_TABS = "NumSpacesForTabs"; 074 private static String fNUM_SPACES_FOR_TABS; 075 private static String fREPLACEMENT_FOR_TAB; 076 private static final String ENCODING = "CharacterSetForGeneratedFiles"; 077 private static String fENCODING; 078 079 private static final ResponsePage FORWARD = new ResponsePage("Files With TAB Chars", "view.jsp", TabsRemoveAction.class); 080 private static final ResponsePage REDIRECT_TO_LISTING = new ResponsePage("TabsListAction.do"); 081 082 private static final Logger fLogger = Util.getLogger(TabsRemoveAction.class); 083 084 private int replaceTabsAndUpdateFileInfo(FileInfo aFile) { 085 assert(aFile.getNumTabs() > 0); 086 int result = aFile.getNumTabs(); 087 File file = new File(aFile.getName().getRawString()); 088 String fileContent = getContentOf(file); 089 String updatedContent = replaceTabs(fileContent); 090 save(updatedContent, file); 091 return result; 092 } 093 094 private String getContentOf(File aFile){ 095 StringBuilder result = new StringBuilder(); 096 try { 097 Scanner scanner = new Scanner(aFile); 098 while ( scanner.hasNextLine() ) { 099 result.append(scanner.nextLine() + NEW_LINE); 100 } 101 scanner.close(); 102 } 103 catch(FileNotFoundException ex){ 104 fLogger.severe("Cannot open file named " + aFile.getName()); 105 } 106 return result.toString(); 107 } 108 109 private static String getReplacementForTabs(){ 110 String result = EMPTY_STRING; 111 Integer numSpaces = Integer.valueOf(fNUM_SPACES_FOR_TABS); 112 for (int idx=1; idx <= numSpaces; ++idx){ 113 result = result + SPACE; 114 } 115 fLogger.fine("Replacement for tabs : " + Util.quote(result)); 116 return result; 117 } 118 119 private String replaceTabs(String aFileContent){ 120 return aFileContent.replace("\t", fREPLACEMENT_FOR_TAB); 121 } 122 123 /** Write the new contents to the file, using encoding specified in web.xml. */ 124 private void save(String aUpdatedContent, File aFile){ 125 fLogger.fine("Updating contents of " + aFile.getAbsolutePath()); 126 Writer output = null; 127 try { 128 FileOutputStream fos = new FileOutputStream(aFile.getAbsolutePath()); 129 OutputStreamWriter out = new OutputStreamWriter(fos, fENCODING); 130 output = new BufferedWriter(out); 131 output.write( aUpdatedContent ); 132 } 133 catch (IOException ex){ 134 fLogger.severe("Replacing tags, but cannot write to file named : " + Util.quote(aFile.getAbsolutePath())); 135 } 136 finally { 137 try { 138 if (output != null) output.close(); 139 } 140 catch (IOException ex) { 141 fLogger.severe("Replacing tags, but cannot close file named : " + Util.quote(aFile.getAbsolutePath())); 142 } 143 } 144 } 145 }