package journal.action; import jargs.gnu.CmdLineParser; import java.io.FileReader; import java.io.IOException; import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; import journal.reader.ActionType; import journal.reader.DataJournalEntry; import journal.reader.Options; import journal.schema.TableVersion; public abstract class BaseRenamerAction extends BaseAction { protected boolean patch = false; protected String fileName = ""; private Map<String, String> renameMap = new HashMap<String, String>(); private Map<String, String> renameLowerMap = new HashMap<String, String>(); private Map<String, Integer> counter = new HashMap<String, Integer>(); private Map<String, Integer> lowerCounter = new HashMap<String, Integer>(); private String name; void putRenamePair(String oldValue, String newValue) { String lowerOldValue = oldValue.toLowerCase(); renameMap.put(oldValue, newValue); renameLowerMap.put(lowerOldValue, newValue); counter.put(oldValue, 0); lowerCounter.put(lowerOldValue, 0); } BaseRenamerAction(String name) { this.name = name; } @Override public void help() { System.err.println("\t\t-f --fileName[=]<file-name> # format: oldname=newname"); System.err.println("\t\t-p --patch"); } @Override public void setDefaultOptions(Options options) { super.setDefaultOptions(options); options.setOptionName(this.getClass().getName(), "fileName", "", new Options.OptionSetter() { public void set(String key, String value) { fileName = value; } } ); options.setOptionName(this.getClass().getName(), "patch", Boolean.FALSE.toString(), new Options.OptionSetter() { public void set(String key, String value) { patch = Boolean.parseBoolean(value); } } ); } @Override public void start() throws Exception { super.start(); } abstract void addArgs(CmdLineParser parser); abstract void processArgs(CmdLineParser parser); @Override public String[] parseArgs(String[] args) { CmdLineParser parser = new CmdLineParser(); CmdLineParser.Option fileNameOption = parser.addStringOption('f', "fileName"); CmdLineParser.Option patchOption = parser.addBooleanOption('p', "patch"); addArgs(parser); try { parser.parse(args); } catch ( CmdLineParser.OptionException e ) { System.err.println(e.getMessage()); help(); System.exit(2); } // allow overriding of options set in the config file fileName = (String) parser.getOptionValue(fileNameOption, fileName); patch = (Boolean) parser.getOptionValue(patchOption, Boolean.valueOf(patch)); // verify that the correct options match up if( ! fileName.equals("") ) { try { readFile(fileName); } catch(IOException e) { throw new IllegalArgumentException("Illegal filename", e); } } processArgs(parser); return parser.getRemainingArgs(); } protected Map<TableVersion, RenameAction> actionMap = new HashMap<TableVersion, RenameAction>(); interface RenameAction { boolean rename(DataJournalEntry data); } protected void readFile(final String f) throws IOException { Properties p = new Properties(); FileReader r = new FileReader(f); p.load(r); for (Enumeration<?> propNames = p.propertyNames(); propNames.hasMoreElements(); ) { String key = (String) propNames.nextElement(); putRenamePair(key, p.getProperty(key)); } } static class CompoundActions implements RenameAction { public boolean rename(DataJournalEntry data) { boolean renamed = false; for( RenameAction action : list) { renamed = renamed | action.rename(data); } return renamed; } private List<RenameAction> list = null; CompoundActions(List<RenameAction> actions) { list = actions; } } class SimpleRenameAction implements RenameAction { private String field; public boolean rename(DataJournalEntry data) { String oldValue = (String) data.getValue(field); Map<String, String> usedMap = renameMap; Map<String, Integer> usedCounter = counter; if( options.isCaseInsensitive() ) { oldValue = oldValue.toLowerCase(); usedMap = renameLowerMap; usedCounter = lowerCounter; } if( usedMap.containsKey(oldValue) ) { String newValue = usedMap.get(oldValue); data.setValue(field, newValue); usedCounter.put(oldValue, usedCounter.get(oldValue) + 1); return true; } return false; } SimpleRenameAction(String field) { this.field = field; } } class FileRenameAction implements RenameAction { private String field; public boolean rename(DataJournalEntry data) { String file = (String) data.getValue(field); for( String key : renameMap.keySet() ) { if( startswith(file, key, options.isCaseInsensitive())) { file = file.replace(key, renameMap.get(key)); data.setValue(field, file); counter.put(key, counter.get(key) + 1); return true; } } return false; } private boolean startswith(String str, String prefix, boolean ignoreCase) { if (str == null || prefix == null) { return (str == null && prefix == null); } if (prefix.length() > str.length()) { return false; } return str.regionMatches(ignoreCase, 0, prefix, 0, prefix.length()); } FileRenameAction(String field) { this.field = field; } } class ClientRenameAction implements RenameAction { private String field; public boolean rename(DataJournalEntry data) { String file = (String) data.getValue(field); Map<String, String> usedMap = renameMap; Map<String, Integer> usedCounter = counter; if( options.isCaseInsensitive() ) { file = file.toLowerCase(); usedMap = renameLowerMap; usedCounter = lowerCounter; } if( file.length() > 0 && file.charAt(0) == '/') { String value = file.substring(2, file.indexOf('/', 2)); if( usedMap.containsKey(value) ) { String newValue = usedMap.get(value); String newValueFile = file.replaceFirst("//"+value+"/", "//" + newValue + "/"); data.setValue(field, newValueFile); usedCounter.put(value, usedCounter.get(value) + 1); return true; } } return false; } ClientRenameAction(String field) { this.field = field; } } static class EmptyAction implements RenameAction { public boolean rename(DataJournalEntry data) { return false; } private EmptyAction() { } public static final EmptyAction instance = new EmptyAction(); } abstract RenameAction getAction(TableVersion version); @Override public void finish() throws Exception { if( options.isVerbose() ) { System.err.println(name + " completed."); Map<String, String> usedMap = renameMap; Map<String, Integer> usedCounter = counter; if( options.isCaseInsensitive() ) { usedMap = renameLowerMap; usedCounter = lowerCounter; } for( String key : usedCounter.keySet()) { System.err.println(key + " -> " + usedMap.get(key) + " : " + usedCounter.get(key)); } } super.finish(); } @Override public void putValue(DataJournalEntry data) throws Exception { TableVersion version = data.getTableVersion(); RenameAction action = getAction(version); DataJournalEntry oldData = null; if( patch ) oldData = new DataJournalEntry(data); boolean renamed = action.rename(data); if( renamed && patch) { out.println(oldData.toJournalString(ActionType.DELETE_VALUE)); } if( renamed || !patch ) { out.println(data.toJournalString()); } } }
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#1 | 24773 | Norman Morse |
Moving journalReader from sven's private branch to perforce_software. This is because journalReader is used in the perfsplit test harness, and should be updated over time. Merging //guest/sven_erik_knop/java/JournalReader/... to //guest/perforce_software/journalReader/... |
||
//guest/sven_erik_knop/java/JournalReader/src/journal/action/BaseRenamerAction.java | |||||
#9 | 8296 | Sven Erik Knop |
Clean-up: instead of casting in every action, cast only once in the dispatcher. Should make code saner and safer. No functional change. |
||
#8 | 8180 | Sven Erik Knop |
BaseRenamerAction now stores oldValue in 2 separate tables, one in lowercase form, the other untouched. This is because at the time when the table is filled we do not know whether the checkpoint is going to be case-insensitive, so we have to store both variants. |
||
#7 | 8027 | Sven Erik Knop |
Moved exceptions to their own package. Enabled new Action FilepathRenamer (not fully tested yet). |
||
#6 | 8023 | Sven Erik Knop |
Complete rewrite of the configuration file, now based on an ini-file format. The ini file has a general [reader] section for settings like verbose, outputFile, case-sensitivity and so on. It also allows to set up a range of Actions and Filters. The section name here is the fully classified class name, followed by settings for the particular actions. An example will make this clearer: ================================================================ [reader] verbose=true [journal.action.UserRenamer] fileName=user.txt patch=True outputFile=user.out [journal.action.ClientRenamer] fileName=client.txt outputFile=client.out patch=true ================================================================ I will provide more example set-ups in the near future. Filters are classes implementing journal.action.Filter (soon to be journal.filter.Filter) which can be chained together and are all executed before the actions. Actions are applied in order that they are given in the config file. |
||
#5 | 8020 | Sven Erik Knop | Replace public Option attributes with setters and getters. | ||
#4 | 8017 | Sven Erik Knop | Fixed patching by adding copy constructor. | ||
#3 | 8016 | Sven Erik Knop | In the middle of refactoring the Renamer classes into the BaseRenamer. | ||
#2 | 8015 | Sven Erik Knop | Now with counter reestablished. | ||
#1 | 8014 | Sven Erik Knop |
Extracted a base class. Not quite finished since I need a counter class as well. |