Mercurial Hosting > nabble
comparison src/nabble/view/web/template/MacroEditorNamespace.java @ 0:7ecd1a4ef557
add content
author | Franklin Schmidt <fschmidt@gmail.com> |
---|---|
date | Thu, 21 Mar 2019 19:15:52 -0600 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:7ecd1a4ef557 |
---|---|
1 package nabble.view.web.template; | |
2 | |
3 import fschmidt.util.java.HtmlUtils; | |
4 import nabble.model.Site; | |
5 import nabble.modules.ModuleManager; | |
6 import nabble.naml.compiler.Command; | |
7 import nabble.naml.compiler.CommandSpec; | |
8 import nabble.naml.compiler.CompileException; | |
9 import nabble.naml.compiler.IPrintWriter; | |
10 import nabble.naml.compiler.Interpreter; | |
11 import nabble.naml.compiler.Macro; | |
12 import nabble.naml.compiler.Meaning; | |
13 import nabble.naml.compiler.Namespace; | |
14 import nabble.naml.compiler.Program; | |
15 import nabble.naml.compiler.Source; | |
16 import nabble.naml.compiler.Usage; | |
17 | |
18 import java.util.ArrayList; | |
19 import java.util.HashMap; | |
20 import java.util.List; | |
21 import java.util.Map; | |
22 import java.util.Set; | |
23 import java.util.regex.Matcher; | |
24 import java.util.regex.Pattern; | |
25 | |
26 | |
27 @Namespace( | |
28 name = "macro_editor", | |
29 global = false | |
30 ) | |
31 public class MacroEditorNamespace { | |
32 | |
33 private Site site; | |
34 private String rootMacroName; | |
35 private String[] baseClassArray; | |
36 private final Macro macro; | |
37 | |
38 private static class SaveResults { | |
39 String error; | |
40 String macroId; | |
41 String base; | |
42 String breadcrumbs; | |
43 } | |
44 | |
45 public MacroEditorNamespace(Site site, String meaningId, String base, String breadcrumbs) { | |
46 this.site = site; | |
47 this.baseClassArray = MacroSourceNamespace.buildBaseClassArray(base); | |
48 this.rootMacroName = getRootMacroName(site.getProgram(), meaningId, base, breadcrumbs); | |
49 if (meaningId == null) { | |
50 macro = null; | |
51 } else { | |
52 Meaning meaning = site.getProgram().getMeaning(meaningId); | |
53 if (meaning == null) | |
54 throw new NullPointerException("Meaning is null for: " + meaningId); | |
55 else if (!(meaning instanceof Macro)) | |
56 throw new NullPointerException("Meaning is not a macro: " + meaningId); | |
57 macro = (Macro) meaning; | |
58 } | |
59 } | |
60 | |
61 private String getRootMacroName(Program program, String meaningId, String base, String breadcrumbs) { | |
62 if (breadcrumbs != null) { | |
63 String firstMeaningId = breadcrumbs.split("-")[0]; | |
64 Meaning meaning = program.getMeaning(firstMeaningId); | |
65 return meaning.getName(); | |
66 } else if (base != null && meaningId != null) { | |
67 return program.getMeaning(meaningId).getName(); | |
68 } | |
69 return null; | |
70 } | |
71 | |
72 public static final CommandSpec CONTENTS = new CommandSpec.Builder() | |
73 .parameters("contents") | |
74 .build(); | |
75 | |
76 public static final CommandSpec save = CONTENTS; | |
77 | |
78 @Command public void save(IPrintWriter out, Interpreter interp) { | |
79 String tweak = getContents(interp); | |
80 | |
81 Map<String, String> originalTweaks = site.getCustomTweaks(); | |
82 Map<String, String> newTweaks = new HashMap<String, String>(originalTweaks); | |
83 if (isEditingCustomTweak()) { | |
84 String macroBody = macro.element.toString(); | |
85 for( Map.Entry<String,String> entry : originalTweaks.entrySet() ) { | |
86 String filename = entry.getKey(); | |
87 String content = entry.getValue(); | |
88 int posStart = content.indexOf(macroBody); | |
89 if (posStart >= 0) { | |
90 SaveResults saveResults = saveTweak(tweak, originalTweaks, newTweaks, filename); | |
91 out.print(HtmlUtils.toJson(saveResults)); | |
92 return; | |
93 } | |
94 } | |
95 } else { | |
96 String macroName = getMacroName(macro, tweak); | |
97 if (macroName == null) { | |
98 out.print("Error: You must specify the name of the macro."); | |
99 return; | |
100 } | |
101 // tries to find a file with the name of the macro | |
102 String file = newTweaks.get(macroName); | |
103 String newFileContents = file == null? tweak : file + "\n\n" + tweak; | |
104 | |
105 SaveResults saveResults = saveTweak(newFileContents, originalTweaks, newTweaks, macroName); | |
106 out.print(HtmlUtils.toJson(saveResults)); | |
107 } | |
108 } | |
109 | |
110 private SaveResults saveTweak(String tweak, Map<String, String> originalTweaks, Map<String, String> newTweaks, String filename) { | |
111 SaveResults saveResults = new SaveResults(); | |
112 try { | |
113 Usage usage = saveAndCheck(newTweaks, filename, tweak); | |
114 saveResults.macroId = getMacroId(filename, tweak); | |
115 // If usage is not null, then it was found while saving the tweak. | |
116 // We send this information back to the page so that the javascript | |
117 // can send the user to a page with navigation links. | |
118 if (usage != null) { | |
119 saveResults.base = MacroSourceNamespace.asBaseParam(usage.baseIds()); | |
120 saveResults.breadcrumbs = MacroSourceNamespace.asBreadcrumbsParam(usage.macroPath()) ; | |
121 } | |
122 } catch(CompileException e) { | |
123 site.setCustomTweaks(originalTweaks); | |
124 saveResults.error = "Error: " + e.getMessage(); | |
125 } | |
126 return saveResults; | |
127 } | |
128 | |
129 private String getMacroName(Macro macro, String tweak) { | |
130 if (macro != null) | |
131 return macro.getName(); | |
132 else { | |
133 Pattern pattern = Pattern.compile("name=\"([^\"]+)\""); | |
134 Matcher matcher = pattern.matcher(tweak); | |
135 if (matcher.find()) | |
136 return matcher.group(1); | |
137 else | |
138 return null; | |
139 } | |
140 } | |
141 | |
142 private Usage saveAndCheck(Map<String, String> newTweaks, String filename, String newFileContents) | |
143 throws CompileException | |
144 { | |
145 newFileContents = trim(newFileContents); | |
146 newTweaks.put(filename, newFileContents); | |
147 | |
148 boolean isCompiledMacro = macro != null && site.getProgram().isCompiled(macro); | |
149 | |
150 site.setCustomTweaks(newTweaks); | |
151 if (rootMacroName != null) { | |
152 try { | |
153 // Tries to compile the macro | |
154 site.getProgram().getTemplate(rootMacroName, this.baseClassArray); | |
155 } catch (CompileException e) { | |
156 if (isCompiledMacro) | |
157 throw e; | |
158 else { | |
159 Program program = site.getProgram(); | |
160 // Let's try to find a usage for this macro for a better check | |
161 if (!NabbleNamespace.isCompiledAll(program)) | |
162 CompileTest.compileAll(program); | |
163 | |
164 Meaning m = program.getMeaning(macro.getId()); | |
165 Set<Usage> usages = program.getUsages(m); | |
166 if (usages == null || usages.size() == 0) | |
167 throw e; | |
168 else { | |
169 Usage usage = usages.iterator().next(); | |
170 program.getTemplate(usage.macroPath().get(0).getName(), usage.baseIds()); | |
171 return usage; | |
172 } | |
173 } | |
174 } | |
175 } else { | |
176 // Simple XML validation | |
177 site.getProgram().getTemplate("do_nothing"); | |
178 } | |
179 return null; | |
180 } | |
181 | |
182 private String getMacroId(String filename, String macroBody) throws CompileException { | |
183 Program program = site.getProgram(); | |
184 if (macro != null && program.getMeaning(macro.getId()) != null) | |
185 return macro.getId(); | |
186 Macro m = program.getMacroWhichOverrides(macro); | |
187 if (m != null) | |
188 return m.getId(); | |
189 List<Source> sources = site.getProgram().getSources(); | |
190 for (Source s : sources) { | |
191 if (s.id.contains(':'+filename)) { | |
192 for (Macro mac : s.getMacros()) { | |
193 if (macroBody.contains(mac.element.toString())) | |
194 return mac.getId(); | |
195 } | |
196 } | |
197 } | |
198 throw new RuntimeException("macroId not found"); | |
199 } | |
200 | |
201 public static final CommandSpec revert = CommandSpec.NO_OUTPUT; | |
202 | |
203 @Command public void revert(IPrintWriter out,Interpreter interp) { | |
204 if (isEditingCustomTweak()) { | |
205 String macroBody = macro.element.toString(); | |
206 Map<String, String> tweaks = site.getCustomTweaks(); | |
207 Map<String, String> reverted = new HashMap<String, String>(tweaks); | |
208 for( Map.Entry<String,String> entry : tweaks.entrySet() ) { | |
209 String name = entry.getKey(); | |
210 String content = entry.getValue(); | |
211 int posStart = content.indexOf(macroBody); | |
212 if (posStart >= 0) { | |
213 int posEnd = posStart + macroBody.length(); | |
214 String revertedContents = content.substring(0, posStart) + "\n" + content.substring(posEnd); | |
215 if (trim(revertedContents).length() == 0) | |
216 reverted.remove(name); | |
217 else | |
218 reverted.put(name, revertedContents); | |
219 site.setCustomTweaks(reverted); | |
220 break; | |
221 } | |
222 } | |
223 } else | |
224 throw new RuntimeException("Not a tweaked macro"); | |
225 } | |
226 | |
227 private String getContents(Interpreter interp) { | |
228 String c = interp.getArgString("contents"); | |
229 c = c.replaceAll("\\u2002", " "); // Converts any   into simple space (copy and paste issue) | |
230 return c; | |
231 } | |
232 | |
233 private String trim(String s) { | |
234 return s.replaceAll("(^\\s+)|(\\s+$)",""); | |
235 } | |
236 | |
237 private boolean isEditingCustomTweak() { | |
238 return macro != null && ModuleManager.isCustomTweak(macro.source); | |
239 } | |
240 } |