Mercurial Hosting > nabble
diff src/nabble/view/web/template/MacroSourceNamespace.java @ 0:7ecd1a4ef557
add content
author | Franklin Schmidt <fschmidt@gmail.com> |
---|---|
date | Thu, 21 Mar 2019 19:15:52 -0600 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/nabble/view/web/template/MacroSourceNamespace.java Thu Mar 21 19:15:52 2019 -0600 @@ -0,0 +1,703 @@ +package nabble.view.web.template; + +import nabble.model.Site; +import nabble.modules.ModuleManager; +import nabble.naml.compiler.Command; +import nabble.naml.compiler.CommandSpec; +import nabble.naml.compiler.CompileException; +import nabble.naml.compiler.IPrintWriter; +import nabble.naml.compiler.Interpreter; +import nabble.naml.compiler.JavaCommand; +import nabble.naml.compiler.Macro; +import nabble.naml.compiler.Meaning; +import nabble.naml.compiler.Namespace; +import nabble.naml.compiler.ParamMeaning; +import nabble.naml.compiler.Program; +import nabble.naml.compiler.ScopedInterpreter; +import nabble.naml.compiler.Source; +import nabble.naml.compiler.Template; +import nabble.naml.compiler.Usage; +import nabble.naml.dom.Attribute; +import nabble.naml.dom.Container; +import nabble.naml.dom.Element; +import nabble.naml.dom.ElementName; +import nabble.naml.dom.EmptyElement; +import nabble.naml.dom.Naml; +import nabble.naml.namespaces.BasicNamespace; +import nabble.naml.namespaces.CommandDoc; +import nabble.naml.namespaces.ListSequence; +import nabble.naml.namespaces.StringList; + +import javax.servlet.ServletException; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + + +@Namespace( + name = "macro_source", + global = true +) +public class MacroSourceNamespace extends ServletNamespaceUtils { + + private Site site; + private String[] baseClassArray; + private String baseClasses; + private String nextBreadcrumbs; + private String[] navigationBreadcrumbs; + private Meaning meaning; + private String macroOverriddenId; + private String macroWhichOverridesId; + + MacroSourceNamespace(Site site, String id, String base, String breadcrumbs) { + this.site = site; + Program program = site.getProgram(); + meaning = program.getMeaning(id); + if( meaning == null ) + throw new NullPointerException("meaning is null for: "+id); + + this.baseClasses = base; + this.baseClassArray = buildBaseClassArray(base); + this.nextBreadcrumbs = buildNextBreadcrumbs(id, breadcrumbs); + + boolean hasBaseClasses = base != null && base.length() > 0; + if (hasBaseClasses) { + try { + Meaning root = program.getMeaning(navigationBreadcrumbs[0]); + program.getTemplate(root.getName(), this.baseClassArray); + } catch (CompileException e) { + throw new RuntimeException(e); + } + } + + if (meaning instanceof Macro) { + Macro macro = (Macro) meaning; + Macro overridden = program.getMacroOverriddenBy(macro); + this.macroOverriddenId = overridden != null? overridden.getId() : null; + Macro overrides = program.getMacroWhichOverrides(macro); + this.macroWhichOverridesId = overrides != null? overrides.getId() : null; + } + } + + static String[] buildBaseClassArray(String base) { + boolean hasBaseClasses = base != null && base.length() > 0; + String[] baseArray = hasBaseClasses? base.split("-") : new String[0]; + + String basicNamespace = nabble.naml.namespaces.BasicNamespace.class.getName(); + String nabbleNamespace = nabble.view.web.template.NabbleNamespace.class.getName(); + + if (baseArray.length >=2 && baseArray[0].equals(basicNamespace) && baseArray[1].equals(nabbleNamespace)) { + return baseArray; + } else { + String[] array = new String[baseArray.length+2]; + array[0] = basicNamespace; + array[1] = nabbleNamespace; + + int i = 2; + for (String className : baseArray) { + array[i++] = className; + } + return array; + } + } + + private String buildNextBreadcrumbs(String id, String breadcrumbs) { + if (breadcrumbs == null) { + navigationBreadcrumbs = new String[] { id }; + return id; + } else { + String[] breadcrumbParts = breadcrumbs.split("-"); + navigationBreadcrumbs = new String[breadcrumbParts.length+1]; + int i = 0; + for (String s : breadcrumbParts) { + navigationBreadcrumbs[i++] = s; + } + navigationBreadcrumbs[i] = meaning.getId(); + return breadcrumbs + '-' + navigationBreadcrumbs[i]; + } + } + + @Command public void id(IPrintWriter out,Interpreter interp) { + out.print(meaning.getId()); + } + + @Command public void name(IPrintWriter out,Interpreter interp) { + out.print(meaning.getName()); + } + + @Command public void macro_overridden_id(IPrintWriter out,Interpreter interp) { + out.print(macroOverriddenId); + } + + @Command public void macro_which_overrides_id(IPrintWriter out,Interpreter interp) { + out.print(macroWhichOverridesId); + } + + @Command public void is_compiled(IPrintWriter out,Interpreter interp) { + out.print(meaning instanceof Macro && site.getProgram().isCompiled(meaning)); + } + + public static final CommandSpec documentation = requiresServletNamespace; + + @Command public void documentation(IPrintWriter out,Interpreter interp) + throws ServletException + { + String name = meaning.getName(); + Collection<String> namespaces = meaning.getRequiredNamespaces(); + Site site = NabbleNamespace.current().site(); + Template template = site.getTemplate(DocNamespace.getDocName(name,namespaces), BasicNamespace.class, NabbleNamespace.class, DocNamespace.class); + if( template == null && !namespaces.isEmpty() ) + template = site.getTemplate(DocNamespace.getDocName(name,Collections.<String>emptySet()), BasicNamespace.class, NabbleNamespace.class, DocNamespace.class); + if( template == null ) { + if( meaning instanceof JavaCommand ) { + JavaCommand jc = (JavaCommand)meaning; + CommandDoc doc = jc.getMethod().getAnnotation(CommandDoc.class); + + String value = doc == null? "" : doc.value(); + String[] params = doc == null? new String[0] : doc.params(); + String[] seeAlso = doc == null? new String[0] : doc.seeAlso(); + + template = site.getTemplate("binary doc", BasicNamespace.class, NabbleNamespace.class, DocNamespace.BinaryDocNamespace.class); + if( template != null ) { + DocNamespace.BinaryDocNamespace ns = new DocNamespace.BinaryDocNamespace(site, jc, value, params, seeAlso); + template.run(out,Collections.<String,Object>emptyMap(),new BasicNamespace(template), new NabbleNamespace(site), ns); + return; + } + } + template = site.getTemplate("doc not found",BasicNamespace.class,NabbleNamespace.class,DocNamespace.class); + } + DocNamespace ns = new DocNamespace(); + template.run(out,Collections.<String,Object>emptyMap(), new BasicNamespace(template), new NabbleNamespace(site), ns); + } + + public static final CommandSpec navigation_breadcrumbs = CommandSpec.DO; + + @Command public void navigation_breadcrumbs(IPrintWriter out, ScopedInterpreter<Parts> interp) { + List<CommandInfo> infos = new ArrayList<CommandInfo>(); + Program program = site.getProgram(); + StringBuilder breadCrumbs = new StringBuilder(); + for (String id : navigationBreadcrumbs) { + Meaning m = program.getMeaning(id); + // Skip broken meanings in the breadcrumbs + if (m != null) + infos.add(new CommandInfo(m,baseClasses,breadCrumbs.length() == 0? null : breadCrumbs.toString())); + if( breadCrumbs.length() > 0 ) + breadCrumbs.append('-'); + breadCrumbs.append(id); + } + out.print(interp.getArg(new Parts(infos),"do")); + } + + @Command public void source(IPrintWriter out, Interpreter interp) { + out.print(getSource(meaning)); + } + + @Command public void source_path(IPrintWriter out, Interpreter interp) { + String path = null; + if (meaning instanceof Macro) { + path = "/template/NamlEditor$ViewFile.jtp?file="+ ((Macro) meaning).source.toString(); + } + out.print(path); + } + + private static String getSource(Meaning m) { + if (m instanceof Macro) { + String source = ((Macro) m).source.toString(); + return source.endsWith(".naml")? source : source + ".naml"; + } + return null; + } + + @Command public void has_macro_overridden(IPrintWriter out,Interpreter interp) { + out.print(macroOverriddenId != null); + } + + @Command public void has_macro_which_overrides(IPrintWriter out,Interpreter interp) { + out.print(macroWhichOverridesId != null); + } + + @Command public void is_override(IPrintWriter out,Interpreter interp) { + out.print(isOverride(meaning)); + } + + private static boolean isOverride(Meaning meaning) { + if( !(meaning instanceof Macro) ) + return false; + return ((Macro) meaning).isOverride(); + } + + @Command public void is_custom_tweak(IPrintWriter out,Interpreter interp) { + out.print(isCustomTweak(meaning)); + } + + @Command public void is_configuration_tweak(IPrintWriter out,Interpreter interp) { + out.print(isConfigurationTweak(meaning)); + } + + private static boolean isCustomTweak(Meaning meaning) { + if( !(meaning instanceof Macro) ) + return false; + Source source = ((Macro) meaning).source; + return ModuleManager.isCustomTweak(source); + } + + private static boolean isConfigurationTweak(Meaning meaning) { + if( !(meaning instanceof Macro) ) + return false; + Source source = ((Macro) meaning).source; + return ModuleManager.isConfigurationTweak(source); + } + + @Command public void is_binary(IPrintWriter out,Interpreter interp) { + out.print(!(meaning instanceof Macro)); + } + + @Command public void tweak_file_contents(IPrintWriter out,Interpreter interp) { + String file = null; + if (meaning instanceof Macro) { + Macro macro = (Macro) meaning; + if (isCustomTweak(meaning)) { + file = macro.source.content; + } else { + file = macro.element.toString(); + file = file.replace("<macro ", "<override_macro "); + file = file.replace("</macro", "</override_macro"); + file = file.replace("<subroutine ", "<override_subroutine "); + file = file.replace("</subroutine", "</override_subroutine"); + file = file.replace("<translation ", "<override_translation "); + file = file.replace("</translation", "</override_translation"); + } + } + out.print(file); + } + + @Command public void macro_opening_tag(IPrintWriter out,Interpreter interp) { + String tag = null; + if (meaning instanceof Macro) { + Macro macro = (Macro) meaning; + tag = macro.element.openingTag(); + if (!isCustomTweak(meaning)) { + tag = tag.replace("<macro ", "<override_macro "); + tag = tag.replace("<subroutine ", "<override_subroutine "); + tag = tag.replace("<translation ", "<override_translation "); + } + } + out.print(tag); + } + + public static final CommandSpec rows = CommandSpec.DO; + + @Command public void rows(IPrintWriter out,ScopedInterpreter<Rows> interp) + throws IOException, ServletException + { + if (meaning instanceof Macro) { + Macro m = (Macro) meaning; + List<CommandInfo> infos = new ArrayList<CommandInfo>(); + List<Parts> rows = new ArrayList<Parts>(); + buildElement(m.element, infos, rows, false); + if (infos.size() > 0) + rows.add(new Parts(infos)); + + Object block = interp.getArg(new Rows(rows, m),"do"); + out.print(block); + } + } + + private void traverse(Naml naml, List<CommandInfo> parts, List<Parts> rows, boolean isAttribute) { + for (Object o : naml) { + if (o instanceof EmptyElement) { + buildEmptyElement((EmptyElement) o, parts, rows, isAttribute); + } else if (o instanceof Container) { + buildElement((Container) o, parts, rows, isAttribute); + } else { + processString(o.toString(), parts, rows); + } + } + } + + private void buildEmptyElement(EmptyElement e, List<CommandInfo> parts, List<Parts> rows, boolean isAttribute) { + startTag(parts, isAttribute); + extractParts(e, parts); + extractAttributes(e, parts, rows); + finishTag(e, parts, rows, isAttribute); + } + + private void buildElement(Element e, List<CommandInfo> parts, List<Parts> rows, boolean isAttribute) { + startTag(parts, isAttribute); + extractParts(e, parts); + + if (e.name().endsWithDot()) + parts.add(new CommandInfo(".")); + + extractAttributes(e, parts, rows); + finishTag(e, parts, rows, isAttribute); + if (e instanceof Container) { + Container container = (Container) e; + traverse(container.contents(), parts, rows, isAttribute); + closingTag(container, isAttribute, parts); + } + } + + private void closingTag(Container e, boolean isAttribute, List<CommandInfo> parts) { + String closingTag = e + .closingTag() + .replace("<",isAttribute?"[":"<") + .replace(">",isAttribute?"]":">"); + parts.add(new CommandInfo(closingTag)); + } + + private void finishTag(Element e, List<CommandInfo> infos, List<Parts> rows, boolean isAttribute) { + processString(e.spaceAtEndOfOpeningTag(), infos, rows); + infos.add(new CommandInfo( + (e instanceof EmptyElement? '/': "") + + (isAttribute? "]":">") + )); + } + + private void startTag(List<CommandInfo> parts, boolean isAttribute) { + parts.add(new CommandInfo(isAttribute?"[":"<")); + } + + private void extractAttributes(Element e, List<CommandInfo> parts, List<Parts> rows) { + for (Attribute attribute : e.attributes()) { + processString(attribute.spaceBeforeName(), parts, rows); + parts.add(new CommandInfo( + attribute.name() + + attribute.spaceAfterName() + + '=' + + attribute.quote()) + ); + traverse(attribute.value(), parts, rows, true); + parts.add(new CommandInfo(String.valueOf(attribute.quote()))); + } + } + + private void extractParts(Element e, List<CommandInfo> parts) { + int i = 0; + List<Macro> currentMacroPath = getCurrentMacroPath(); + Usage currentUsage = site.getProgram().getUsage(baseClassArray, currentMacroPath); + for (ElementName.Part p : e.name().parts()) { + if (i++ > 0) + parts.add(new CommandInfo(".")); + Meaning meaning = site.getProgram().getMeaning(p, currentUsage); + if (meaning != null) { + boolean isArgument = meaning instanceof ParamMeaning; + boolean isOverridden = "overridden".equals(p.text()); + if (isArgument || isOverridden) { + parts.add(new CommandInfo(p.text())); + } else { + parts.add(new CommandInfo(meaning, baseClasses, nextBreadcrumbs)); + } + } else + parts.add(new CommandInfo(p.text())); + } + } + + private List<Macro> getCurrentMacroPath() { + List<Macro> macroPath = new ArrayList<Macro>(); + Program program = site.getProgram(); + for (String id : navigationBreadcrumbs) { + macroPath.add((Macro) program.getMeaning(id)); + } + return macroPath; + } + + private void processString(String e, List<CommandInfo> infos, List<Parts> rows) { + StringBuilder builder = new StringBuilder(); + int i = 0; + while (i < e.length()) { + char c = e.charAt(i); + if (c == ' ') + builder.append(" "); + else if (c == '\t') + builder.append(" "); + else if ((c == '\r' && e.charAt(i+1) == '\n') || c == '\n') { + if (builder.length() > 0) { + infos.add(new CommandInfo(builder.toString())); + builder.setLength(0); + } + rows.add(new Parts(new ArrayList<CommandInfo>(infos))); + infos.clear(); + if (c == '\r') + i++; // CRLF + } else if (c == '<') { + builder.append("<"); + } else if (c == '>') { + builder.append(">"); + } else if (c == '&') { + builder.append("&"); + } else + builder.append(c); + i++; + } + if (builder.length() > 0) + infos.add(new CommandInfo(builder.toString())); + } + + static String csv(Collection<String> strings) { + StringBuilder b = new StringBuilder(); + for (String s : strings) { + if (b.length() > 0) + b.append(", "); + b.append(s); + } + return b.toString(); + } + + @Namespace ( + name = "macro_row_list", + global = true + ) + public static final class Rows extends ListSequence<Parts> + { + + private final Macro macro; + + Rows(List<Parts> rows, Macro macro) { + super(rows); + this.macro = macro; + } + + @Command public void line_number(IPrintWriter out,Interpreter interp) { + out.print(index + 1); + } + + @Command public void file_line_number(IPrintWriter out,Interpreter interp) { + out.print(macro.element.lineNumber() + index + 1); + } + + public static final CommandSpec current_row = CommandSpec.DO; + + @Command public void current_row(IPrintWriter out,ScopedInterpreter<Parts> interp) { + out.print(interp.getArg(get(),"do")); + } + + @Command public void next_row(IPrintWriter out,Interpreter interp) { + next_element(out,interp); + } + } + + @Namespace ( + name = "macro_parts_list", + global = true + ) + public static final class Parts extends ListSequence<CommandInfo> { + + Parts(List<CommandInfo> infos) { + super(infos); + } + + public static final CommandSpec parts = CommandSpec.DO; + + @Command public void parts(IPrintWriter out,ScopedInterpreter<CommandInfo> interp) { + out.print( interp.getArg(get(),"do") ); + } + + @Command public void is_blank(IPrintWriter out, Interpreter interp) { + out.print(elements.size() == 0); + } + } + + public static final CommandSpec macro_usages = CommandSpec.DO; + + @Command public void macro_usages(IPrintWriter out,ScopedInterpreter<MacroUsages> interp) + throws IOException, ServletException, CompileException + { + List<Commands> macros = new ArrayList<Commands>(); + Set<Usage> usages = site.getProgram().getUsages(meaning); + if (usages == null) + usages = new HashSet<Usage>(); + for (Usage u : usages) { + List<CommandInfo> infos = new ArrayList<CommandInfo>(); + StringBuilder breadcrumbs = new StringBuilder(); + String usageBase = asBaseParam(u.baseIds()); + for (Macro m : u.macroPath()) { + infos.add(new CommandInfo(m, usageBase, breadcrumbs.length() == 0? null : breadcrumbs.toString())); + + if (breadcrumbs.length() > 0) + breadcrumbs.append('-'); + breadcrumbs.append(m.getId()); + } + infos.add(new CommandInfo(meaning, usageBase, breadcrumbs.toString())); + macros.add(new Commands(infos)); + } + Object block = interp.getArg(new MacroUsages(macros),"do"); + out.print(block); + } + + static String asBaseParam(String[] base) { + StringBuilder b = new StringBuilder(); + for (String c : base) { + if (b.length() > 0) + b.append('-'); + b.append(c); + } + return b.toString(); + } + + static String asBreadcrumbsParam(List<Macro> macroPath) { + StringBuilder breadcrumbs = new StringBuilder(); + for (Macro p : macroPath) { + if (breadcrumbs.length() > 0) + breadcrumbs.append('-'); + breadcrumbs.append(p.getId()); + } + return breadcrumbs.toString(); + } + + @Namespace ( + name = "macro_usages", + global = true + ) + public static final class MacroUsages extends ListSequence<Commands> + { + public MacroUsages(List<Commands> elements) { + super(elements); + } + + public static final CommandSpec current_usage = CommandSpec.DO; + + @Command public void current_usage(IPrintWriter out,ScopedInterpreter<Commands> interp) { + out.print(interp.getArg(get(),"do")); + } + } + + @Namespace ( + name = "command_list", + global = true + ) + public static final class Commands extends ListSequence<CommandInfo> + { + public Commands(List<CommandInfo> elements) { + super(elements); + } + + public static final CommandSpec current_command = CommandSpec.DO; + + @Command public void current_command(IPrintWriter out,ScopedInterpreter<CommandInfo> interp) { + out.print(interp.getArg(get(),"do")); + } + } + + @Namespace ( + name = "command_info", + global = false + ) + public static final class CommandInfo { + private final String id; + private final String name; + private final String tag; + private final String source; + private final int fileLineNumber; + private final String baseClasses; + private final String breadcrumbs; + private final String requiredNamespaces; + private final boolean isCustomTweak; + private final boolean isMacro; + private final boolean isBinary; + private final String namespaceClass; + private final String[] parameterNames; + + static final Comparator<? super CommandInfo> MACRO_NAME_COMPARATOR = new Comparator<MacroSourceNamespace.CommandInfo>() { + public int compare(MacroSourceNamespace.CommandInfo o1, MacroSourceNamespace.CommandInfo o2) { + return o1.name.compareTo(o2.name); + } + }; + + public CommandInfo(String name) { + this.id = null; + this.tag = name; + this.name = name; + this.isMacro = false; + this.isBinary = false; + this.source = null; + this.fileLineNumber = 0; + this.baseClasses = null; + this.breadcrumbs = null; + this.requiredNamespaces = null; + this.isCustomTweak = false; + this.namespaceClass = null; + this.parameterNames = null; + } + + public CommandInfo(Meaning meaning, String baseClasses, String breadcrumbs) { + this.id = meaning.getId(); + this.name = meaning.getName(); + this.tag = this.name.startsWith("translation:")? "t" : this.name; + this.isMacro = meaning instanceof Macro; + this.isBinary = meaning instanceof JavaCommand; + this.source = getSource(meaning); + this.fileLineNumber = isMacro? ((Macro) meaning).element.lineNumber() + 1 : 0; + this.baseClasses = baseClasses; + this.breadcrumbs = breadcrumbs; + this.requiredNamespaces = csv(meaning.getRequiredNamespaces()); + this.isCustomTweak = isCustomTweak(meaning); + this.namespaceClass = meaning instanceof JavaCommand? ((JavaCommand) meaning).getMethod().getDeclaringClass().getSimpleName() : null; + this.parameterNames = isMacro? ((Macro) meaning).getParameterNames() : meaning instanceof JavaCommand? ((JavaCommand) meaning).getParameterNames() : null; + } + + @Command("id") public void _id(IPrintWriter out,Interpreter interp) { + out.print(id); + } + + @Command("name") public void _name(IPrintWriter out,Interpreter interp) { + out.print(name); + } + + @Command("tag") public void _tag(IPrintWriter out,Interpreter interp) { + out.print(tag); + } + + @Command("source") public void _source(IPrintWriter out,Interpreter interp) { + out.print(source); + } + + @Command public void naml_breadcrumbs(IPrintWriter out,Interpreter interp) { + out.print(breadcrumbs); + } + + @Command public void base(IPrintWriter out,Interpreter interp) { + out.print(baseClasses); + } + + @Command public void file_line_number(IPrintWriter out,Interpreter interp) { + out.print(fileLineNumber); + } + + @Command public void required_namespaces(IPrintWriter out,Interpreter interp) { + out.print(requiredNamespaces); + } + + @Command public void is_custom_tweak(IPrintWriter out,Interpreter interp) { + out.print(isCustomTweak); + } + + @Command public void is_macro(IPrintWriter out,Interpreter interp) { + out.print(isMacro); + } + + @Command public void namespace_class(IPrintWriter out,Interpreter interp) { + out.print(namespaceClass); + } + + @Command public void is_binary(IPrintWriter out,Interpreter interp) { + out.print(isBinary); + } + + @Command public void has_parameters(IPrintWriter out,Interpreter interp) { + out.print(parameterNames != null && parameterNames.length > 0); + } + + public static final CommandSpec parameter_names = CommandSpec.DO; + + @Command public void parameter_names(IPrintWriter out, ScopedInterpreter<StringList> interp) { + out.print(interp.getArg(new StringList(Arrays.asList(parameterNames)),"do")); + } + } +}