Mercurial Hosting > nabble
view 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 source
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")); } } }