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
This commit is contained in:
@@ -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 <code>previouslyGenerated</code> with marked sections updated from the same marked
|
||||
* sections in <code>newlyGenerated</code> Everything outside these sections in
|
||||
* <code>previouslyGenerated</code> is returned as is. A marked section starts with
|
||||
* "// GENERATED <name> START" and ends with "// GENERATED <name> END"
|
||||
* sections in <code>newlyGenerated</code>. Everything outside these sections in
|
||||
* <code>previouslyGenerated</code> is returned as is. A marked section starts with <code>//
|
||||
* GENERATED {name} START</code> and ends with <code>// GENERATED {name} END</code><p>
|
||||
*
|
||||
* If <code>previouslyGenerated</code> has a generated section replaced with <code>//
|
||||
* GENERATED {name} DISABLED</code>, 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<String, String> sections = Maps.newLinkedHashMap();
|
||||
Map<String, Section> sections = Maps.newLinkedHashMap();
|
||||
Matcher m = _sectionDelimiter.matcher(newlyGenerated);
|
||||
while (m.find()) {
|
||||
Tuple<String, String> 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<Entry<String, String>> secs = sections.entrySet().iterator();
|
||||
int currentStart = 0;
|
||||
while (m.find()) {
|
||||
merged.append(previouslyGenerated.substring(currentStart, m.start()));
|
||||
Tuple<String, String> 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<String, String> 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<String, String> 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. <code>m</code> is at the end of the section when this returns.
|
||||
*/
|
||||
protected Tuple<String, String> 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");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user