From 4d163ffd7f958e77f0c6a76f082f1c92fa1bb638 Mon Sep 17 00:00:00 2001 From: Charlie Groves Date: Wed, 15 Dec 2010 23:46:56 +0000 Subject: [PATCH] Allow generated sections to be turned off. If a generated section like // GENERATED BLAH START var s: String = "I am some awesome stuff you didn't have to write"; // GENERATED BLAH END is replaced with // GENERATED BLAH DISABLED that generated section will no longer be merged in. This currently allows the class declaration to be customized in the actionscript stuff, if it needs to extend or implement something that can't be inferred from the Java class. If someone's feeling frisky, they could split the streamable section into sections for readObject, writeObject and fields to allow for read and write customization. This also fixes dropping no-longer generated sections and allows for sections to be reordered in the source. git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@6383 542714f4-19e9-0310-aa3c-eee0fc999fb1 --- .../presents/tools/GeneratedSourceMerger.java | 86 +++++++++++-------- .../tools/GeneratedSourceMergerTest.java | 61 +++++++++++++ 2 files changed, 110 insertions(+), 37 deletions(-) create mode 100644 src/test/java/com/threerings/presents/tools/GeneratedSourceMergerTest.java diff --git a/src/main/java/com/threerings/presents/tools/GeneratedSourceMerger.java b/src/main/java/com/threerings/presents/tools/GeneratedSourceMerger.java index 42e5aaf1d..f57c195c3 100644 --- a/src/main/java/com/threerings/presents/tools/GeneratedSourceMerger.java +++ b/src/main/java/com/threerings/presents/tools/GeneratedSourceMerger.java @@ -21,15 +21,12 @@ package com.threerings.presents.tools; -import java.util.Iterator; import java.util.Map; -import java.util.Map.Entry; import java.util.regex.Matcher; import java.util.regex.Pattern; import com.google.common.base.Preconditions; import com.google.common.collect.Maps; -import com.samskivert.util.Tuple; /** * Merges updates to a generated source file into a previously generated version of that source @@ -39,54 +36,54 @@ public class GeneratedSourceMerger { /** * Returns previouslyGenerated with marked sections updated from the same marked - * sections in newlyGenerated Everything outside these sections in - * previouslyGenerated is returned as is. A marked section starts with - * "// GENERATED START" and ends with "// GENERATED END" + * sections in newlyGenerated. Everything outside these sections in + * previouslyGenerated is returned as is. A marked section starts with // + * GENERATED {name} START and ends with // GENERATED {name} END

+ * + * If previouslyGenerated has a generated section replaced with // + * GENERATED {name} DISABLED, that section will no longer be updated. */ public String merge (String newlyGenerated, String previouslyGenerated) throws Exception { // Extract the generated section names from the output and make sure they're all matched - Map sections = Maps.newLinkedHashMap(); + Map sections = Maps.newLinkedHashMap(); Matcher m = _sectionDelimiter.matcher(newlyGenerated); while (m.find()) { - Tuple section = extractGeneratedSection(m, newlyGenerated); - Preconditions.checkArgument(!sections.containsKey(section.left), - "Section '%s' used more than once", section.left); - sections.put(section.left, section.right); + Section section = extractGeneratedSection(m, newlyGenerated); + Preconditions.checkArgument(!sections.containsKey(section.name), + "Section '%s' used more than once", section.name); + sections.put(section.name, section); } // Merge with the previously generated source StringBuilder merged = new StringBuilder(); m = _sectionDelimiter.matcher(previouslyGenerated); - Iterator> secs = sections.entrySet().iterator(); int currentStart = 0; while (m.find()) { merged.append(previouslyGenerated.substring(currentStart, m.start())); - Tuple existingSection = - extractGeneratedSection(m, previouslyGenerated); - // Allow generated sections to be dropped in the template, but warn in case something - // odd's happening - if (!secs.hasNext()) { + Section existingSection = extractGeneratedSection(m, previouslyGenerated); + Section newSection = sections.remove(existingSection.name); + if (newSection == null) { + // Allow generated sections to be dropped in the template, but warn in case + // something odd's happening System.err.println("Dropping previously generated section '" + m.group(1) + "' that's no longer generated by the template"); + } else if (existingSection.disabled) { + // If the existing code disables this generation, add that disabled comment + merged.append(existingSection.contents); + } else { + // Otherwise pop in the newly generated code in the place of what was there before + merged.append(newSection.contents); } - // However, we don't allow sections to be removed or reordered in the generated code - Entry newSection = secs.next(); - Preconditions.checkArgument(newSection.getKey().equals(existingSection.left), - "Generated code expected '%s' section, but encountered '%s'", newSection.getKey(), - existingSection.left); - // Pop in the newly generated code in the place of what was there before - merged.append(newSection.getValue()); currentStart = m.end(); } // Add generated sections that weren't present in the old output before the last // non-generated code. It's a 50-50 shot, so warn when this happens - while(secs.hasNext()) { - Entry newSection = secs.next(); + for (Section newSection : sections.values()) { System.err.println("Adding previously missing generated section '" - + newSection.getKey() + "' before the last non-generated text"); - merged.append(newSection.getValue()); + + newSection.name + "' before the last non-generated text"); + merged.append(newSection.contents); } // Add any text past the last previously generated section merged.append(previouslyGenerated.substring(currentStart)); @@ -98,20 +95,35 @@ public class GeneratedSourceMerger * Returns a section name and its contents from the given matcher pointing to the start of a * section. m is at the end of the section when this returns. */ - protected Tuple extractGeneratedSection (Matcher m, String input) + protected Section extractGeneratedSection (Matcher m, String input) { int startIdx = m.start(); - String startName = m.group(1); + String name = m.group(1); + if (m.group(2).equals("DISABLED")) { + return new Section(name, input.substring(startIdx, m.end()), true); + } Preconditions.checkArgument(m.group(2).equals("START"), "'%s' END without START", - startName); - Preconditions.checkArgument(m.find(), "'%s' START without END", startName); + name); + Preconditions.checkArgument(m.find(), "'%s' START without END", name); String endName = m.group(1); Preconditions.checkArgument(m.group(2).equals("END"), - "'%s' START after '%s' START", endName, startName); - Preconditions.checkArgument(endName.equals(startName), - "'%s' END after '%s' START", endName, startName); - return Tuple.newTuple(startName, input.substring(startIdx, m.end())); + "'%s' START after '%s' START", endName, name); + Preconditions.checkArgument(endName.equals(name), + "'%s' END after '%s' START", endName, name); + return new Section(name, input.substring(startIdx, m.end()), false); } - protected final Pattern _sectionDelimiter = Pattern.compile(" *// GENERATED (\\w+) (START|END)\n"); + protected static class Section { + public final String name, contents; + public final boolean disabled; + + public Section (String name, String contents, boolean disabled) { + this.name = name; + this.contents = contents; + this.disabled = disabled; + } + } + + protected final Pattern _sectionDelimiter = + Pattern.compile(" *// GENERATED (\\w+) (START|END|DISABLED)\n"); } diff --git a/src/test/java/com/threerings/presents/tools/GeneratedSourceMergerTest.java b/src/test/java/com/threerings/presents/tools/GeneratedSourceMergerTest.java new file mode 100644 index 000000000..459718829 --- /dev/null +++ b/src/test/java/com/threerings/presents/tools/GeneratedSourceMergerTest.java @@ -0,0 +1,61 @@ +package com.threerings.presents.tools; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +public class GeneratedSourceMergerTest +{ + @Test + public void mergeNothing () + throws Exception + { + new GeneratedSourceMerger().merge("", ""); + } + + @Test + public void mergeUpdatedSection () + throws Exception + { + String modified = before + "// GENERATED VARIABLE START\n" + + "var s :String = 'byte';\n" + + "// GENERATED VARIABLE END\n" + after; + + assertEquals(generated, new GeneratedSourceMerger().merge(generated, modified)); + } + + @Test + public void mergeMissingSection () + throws Exception + { + assertEquals(section + before, new GeneratedSourceMerger().merge(generated, before)); + } + + @Test + public void ignoreDisabledSection () + throws Exception + { + String disabled = "// GENERATED VARIABLE DISABLED\n" + before; + assertEquals(disabled, new GeneratedSourceMerger().merge(generated, disabled)); + } + + @Test + public void dropOldSection () + throws Exception + { + String previous = "// GENERATED PREVIOUS START\n" + + "var noLongerNeeded :String = 'hi';\n" + + "// GENERATED PREVIOUS END\n" + + generated; + assertEquals(generated, new GeneratedSourceMerger().merge(generated, previous)); + } + + String section = "// GENERATED VARIABLE START\n" + + "var s :String = 'hi';\n" + + "// GENERATED VARIABLE END\n"; + + String before = "var r :int = 7;\n"; + String after = "var t :Array = [];\n"; + String generated = before + section + after; + +}