From 3d6236242f230e83de95bb18ef7cb7e8e6df2c33 Mon Sep 17 00:00:00 2001 From: nbaars Date: Sat, 10 Jan 2015 10:12:08 +0100 Subject: [PATCH] properties loaded from plugin --- .../org/owasp/webgoat/plugins/Plugin.java | 154 +++++++++--------- .../webgoat/plugins/PluginFileUtils.java | 22 +++ .../owasp/webgoat/plugins/PluginsLoader.java | 9 +- .../plugins/ResourceBundleClassLoader.java | 33 ++++ .../org/owasp/webgoat/session/Course.java | 8 +- .../owasp/webgoat/session/WebgoatContext.java | 14 +- .../org/owasp/webgoat/util/LabelProvider.java | 6 +- src/main/resources/WebGoatLabels.properties | 21 --- .../resources/WebGoatLabels_de.properties | 8 - .../resources/WebGoatLabels_fr.properties | 7 - .../resources/WebGoatLabels_ru.properties | 7 - src/main/webapp/js/ace/mode-praat.js | 2 +- src/main/webapp/js/ace/snippets/jsp.js | 2 +- 13 files changed, 165 insertions(+), 128 deletions(-) create mode 100644 src/main/java/org/owasp/webgoat/plugins/PluginFileUtils.java create mode 100644 src/main/java/org/owasp/webgoat/plugins/ResourceBundleClassLoader.java delete mode 100644 src/main/resources/WebGoatLabels.properties delete mode 100644 src/main/resources/WebGoatLabels_de.properties delete mode 100644 src/main/resources/WebGoatLabels_fr.properties delete mode 100644 src/main/resources/WebGoatLabels_ru.properties diff --git a/src/main/java/org/owasp/webgoat/plugins/Plugin.java b/src/main/java/org/owasp/webgoat/plugins/Plugin.java index 032c7f914..3cecb1981 100644 --- a/src/main/java/org/owasp/webgoat/plugins/Plugin.java +++ b/src/main/java/org/owasp/webgoat/plugins/Plugin.java @@ -5,98 +5,104 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.util.StringUtils; +import java.io.ByteArrayOutputStream; import java.io.File; +import java.io.IOException; +import java.nio.file.Files; import java.nio.file.Path; -import java.util.ArrayList; +import java.nio.file.StandardOpenOption; import java.util.HashMap; import java.util.List; import java.util.Map; +import static org.owasp.webgoat.plugins.PluginFileUtils.fileEndsWith; +import static org.owasp.webgoat.plugins.PluginFileUtils.hasParentDirectoryWithName; + public class Plugin { - private static final Logger logger = LoggerFactory.getLogger(Plugin.class); - private final Class lesson; + private static final String NAME_LESSON_SOLUTION_DIRECTORY = "lessonSolutions"; + private static final String NAME_LESSON_PLANS_DIRECTORY = "lessonPlans"; + private static final String NAME_LESSON_I18N_DIRECTORY = "i18n"; + private final Logger logger = LoggerFactory.getLogger(Plugin.class); private final Path pluginDirectory; - private final Map solutionLanguageFiles; - private final Map lessonPlansLanguageFiles; - private final File lessonSourceFile; + + private Class lesson; + private Map solutionLanguageFiles = new HashMap<>(); + private Map lessonPlansLanguageFiles = new HashMap<>(); + private File lessonSourceFile; public static class PluginLoadingFailure extends RuntimeException { public PluginLoadingFailure(String message) { super(message); } - } - public static class Builder { - - private Path pluginDirectory; - private Class lesson; - private final List loadedClasses = new ArrayList(); - private final Map solutionLanguageFiles = new HashMap<>(); - private final Map lessonPlansLanguageFiles = new HashMap<>(); - private File javaSource; - - public Builder loadClasses(Map classes) { - for (Map.Entry clazz : classes.entrySet() ) { - loadClass(clazz.getKey(), clazz.getValue()); - } - return this; - } - - public Builder loadClass(String name, byte[] classFile) { - ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); - PluginClassLoader pluginClassLoader = new PluginClassLoader(contextClassLoader, classFile); - try { - String realClassName = name.replaceFirst("/", "").replaceAll("/", ".").replaceAll(".class", ""); - Class clazz = pluginClassLoader.loadClass(realClassName); - if (AbstractLesson.class.isAssignableFrom(clazz)) { - this.lesson = clazz; - } - loadedClasses.add(clazz.getName()); - } catch (ClassNotFoundException e) { - logger.error("Unable to load class {}", name); - } - return this; - } - - public Builder setBaseDirectory(Path pluginDirectory) { - this.pluginDirectory = pluginDirectory; - return this; - } - - public Plugin build() { - if ( lesson == null ) { - throw new PluginLoadingFailure(String.format("Lesson class not found, following classes were detected in the plugin: %s", - StringUtils.collectionToCommaDelimitedString(loadedClasses))); - } - return new Plugin(this.lesson, pluginDirectory, lessonPlansLanguageFiles, solutionLanguageFiles, javaSource); - } - - public void loadFiles(List files) { - for (Path file : files) { - if (file.getFileName().toString().endsWith(".html") && file.getParent().getParent().getFileName().toString() - .endsWith("lessonSolutions")) { - solutionLanguageFiles.put(file.getParent().getFileName().toString(), file.toFile()); - } - if (file.getFileName().toString().endsWith(".html") && file.getParent().getParent().getFileName().toString() - .endsWith("lessonPlans")) { - lessonPlansLanguageFiles.put(file.getParent().getFileName().toString(), file.toFile()); - } - if ( file.getFileName().toString().endsWith(".java")) { - javaSource = file.toFile(); - } - } + public PluginLoadingFailure(String message, Exception e) { + super(message, e); } } - public Plugin(Class lesson, Path pluginDirectory, Map lessonPlansLanguageFiles, - Map solutionLanguageFiles, File lessonSourceFile) { - this.lesson = lesson; + public Plugin(Path pluginDirectory) { this.pluginDirectory = pluginDirectory; - this.lessonPlansLanguageFiles = lessonPlansLanguageFiles; - this.solutionLanguageFiles = solutionLanguageFiles; - this.lessonSourceFile = lessonSourceFile; + } + + public void loadClasses(Map classes) { + for (Map.Entry clazz : classes.entrySet()) { + loadClass(clazz.getKey(), clazz.getValue()); + } + if (lesson == null) { + throw new PluginLoadingFailure(String + .format("Lesson class not found, following classes were detected in the plugin: %s", + StringUtils.collectionToCommaDelimitedString(classes.keySet()))); + } + } + + private void loadClass(String name, byte[] classFile) { + ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); + PluginClassLoader pluginClassLoader = new PluginClassLoader(contextClassLoader, classFile); + try { + String realClassName = name.replaceFirst("/", "").replaceAll("/", ".").replaceAll(".class", ""); + Class clazz = pluginClassLoader.loadClass(realClassName); + if (AbstractLesson.class.isAssignableFrom(clazz)) { + this.lesson = clazz; + } + } catch (ClassNotFoundException e) { + logger.error("Unable to load class {}", name); + } + } + + public void loadFiles(List files) { + for (Path file : files) { + if (fileEndsWith(file, ".html") && hasParentDirectoryWithName(file, NAME_LESSON_SOLUTION_DIRECTORY)) { + solutionLanguageFiles.put(file.getParent().getFileName().toString(), file.toFile()); + } + if (fileEndsWith(file, ".html") && hasParentDirectoryWithName(file, NAME_LESSON_PLANS_DIRECTORY)) { + lessonPlansLanguageFiles.put(file.getParent().getFileName().toString(), file.toFile()); + } + if (fileEndsWith(file, ".java")) { + lessonSourceFile = file.toFile(); + } + if (fileEndsWith(file, ".properties") && hasParentDirectoryWithName(file, NAME_LESSON_I18N_DIRECTORY)) { + try { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + Files.copy(file, bos); + Path propertiesPath = createPropertiesDirectory(); + ResourceBundleClassLoader.setPropertiesPath(propertiesPath); + Files.write(propertiesPath.resolve(file.getFileName()), bos.toByteArray(), + StandardOpenOption.CREATE, StandardOpenOption.APPEND); + } catch (IOException io) { + throw new PluginLoadingFailure("Property file detected, but unable to copy the properties", io); + } + } + } + } + + private Path createPropertiesDirectory() throws IOException { + if (Files.exists(pluginDirectory.resolve(NAME_LESSON_I18N_DIRECTORY))) { + return pluginDirectory.resolve(NAME_LESSON_I18N_DIRECTORY); + } else { + return Files.createDirectory(pluginDirectory.resolve(NAME_LESSON_I18N_DIRECTORY)); + } } public Class getLesson() { @@ -107,7 +113,9 @@ public class Plugin { return this.solutionLanguageFiles; } - public File getLessonSource() { return lessonSourceFile; } + public File getLessonSource() { + return lessonSourceFile; + } public Map getLessonPlans() { return this.lessonPlansLanguageFiles; diff --git a/src/main/java/org/owasp/webgoat/plugins/PluginFileUtils.java b/src/main/java/org/owasp/webgoat/plugins/PluginFileUtils.java new file mode 100644 index 000000000..315ac0505 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/plugins/PluginFileUtils.java @@ -0,0 +1,22 @@ +package org.owasp.webgoat.plugins; + + +import java.nio.file.Path; + +public class PluginFileUtils { + + public static boolean fileEndsWith(Path p, String s) { + return p.getFileName().toString().endsWith(s); + } + + public static boolean hasParentDirectoryWithName(Path p, String s) { + if (p == null || p.getParent() == null || p.getRoot().equals(p.getParent())) { + return false; + } + if (p.getParent().getFileName().toString().equals(s)) { + return true; + } + return hasParentDirectoryWithName(p.getParent(), s); + } + +} diff --git a/src/main/java/org/owasp/webgoat/plugins/PluginsLoader.java b/src/main/java/org/owasp/webgoat/plugins/PluginsLoader.java index dc0bb8315..ac4fa5566 100644 --- a/src/main/java/org/owasp/webgoat/plugins/PluginsLoader.java +++ b/src/main/java/org/owasp/webgoat/plugins/PluginsLoader.java @@ -31,11 +31,10 @@ public class PluginsLoader implements Runnable { try { PluginExtractor extractor = new PluginExtractor(file); extractor.extract(); - Plugin.Builder builder = new Plugin.Builder(); - builder.loadClasses(extractor.getClasses()); - builder.loadFiles(extractor.getFiles()); - builder.setBaseDirectory(extractor.getBaseDirectory()); - plugins.add(builder.build()); + Plugin plugin = new Plugin(extractor.getBaseDirectory()); + plugin.loadClasses(extractor.getClasses()); + plugin.loadFiles(extractor.getFiles()); + plugins.add(plugin); } catch (Plugin.PluginLoadingFailure e) { logger.error("Unable to load plugin, continue reading others..."); } diff --git a/src/main/java/org/owasp/webgoat/plugins/ResourceBundleClassLoader.java b/src/main/java/org/owasp/webgoat/plugins/ResourceBundleClassLoader.java new file mode 100644 index 000000000..6f69f7ce1 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/plugins/ResourceBundleClassLoader.java @@ -0,0 +1,33 @@ +package org.owasp.webgoat.plugins; + +import java.io.IOException; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; + +public class ResourceBundleClassLoader { + + private final static ResourceBundleClassLoader classLoader = new ResourceBundleClassLoader(); + private Path propertiesPath; + + private ResourceBundleClassLoader() { + } + + public static void setPropertiesPath(Path path) { + classLoader.propertiesPath = path; + } + + public static ClassLoader createPropertyFilesClassLoader(ClassLoader parentClassLoader) { + final List urls = new ArrayList<>(); + + try { + urls.add(classLoader.propertiesPath.toUri().toURL()); + } catch (IOException e) { + throw new Plugin.PluginLoadingFailure("Unable to load the properties for the classloader", e); + } + return new URLClassLoader(urls.toArray(new URL[urls.size()]), Thread.currentThread().getContextClassLoader()); + } + +} \ No newline at end of file diff --git a/src/main/java/org/owasp/webgoat/session/Course.java b/src/main/java/org/owasp/webgoat/session/Course.java index 76c56527f..02bf24f72 100644 --- a/src/main/java/org/owasp/webgoat/session/Course.java +++ b/src/main/java/org/owasp/webgoat/session/Course.java @@ -11,6 +11,7 @@ import org.slf4j.LoggerFactory; import javax.servlet.ServletContext; import java.io.File; import java.io.IOException; +import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.Collections; @@ -313,7 +314,9 @@ public class Course { logger.error("Plugins directory {} not found", path); return; } - List plugins = new PluginsLoader(Paths.get(path)).loadPlugins(); + Path pluginDirectory = Paths.get(path); + webgoatContext.setPluginDirectory(pluginDirectory); + List plugins = new PluginsLoader(pluginDirectory).loadPlugins(); for (Plugin plugin : plugins) { try { Class c = plugin.getLesson(); @@ -330,7 +333,7 @@ public class Course { for(Map.Entry lessonPlan : plugin.getLessonPlans().entrySet()) { lesson.setLessonPlanFileName(lessonPlan.getKey(), lessonPlan.getValue().toString()); } - lesson.setLessonSolutionFileName(plugin.getLessonPlans().get("en").toString()); + lesson.setLessonSolutionFileName(plugin.getLessonSolutions().get("en").toString()); lesson.setSourceFileName(plugin.getLessonSource().toString()); } catch (Exception e) { logger.error("Error in loadLessons: ", e); @@ -433,4 +436,5 @@ public class Course { //loadResources(); } + } diff --git a/src/main/java/org/owasp/webgoat/session/WebgoatContext.java b/src/main/java/org/owasp/webgoat/session/WebgoatContext.java index 1d3fb8266..9cb27494f 100644 --- a/src/main/java/org/owasp/webgoat/session/WebgoatContext.java +++ b/src/main/java/org/owasp/webgoat/session/WebgoatContext.java @@ -1,9 +1,11 @@ package org.owasp.webgoat.session; -import javax.servlet.http.HttpServlet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.servlet.http.HttpServlet; +import java.nio.file.Path; + public class WebgoatContext { final Logger logger = LoggerFactory.getLogger(WebgoatContext.class); @@ -80,6 +82,8 @@ public class WebgoatContext { private String defaultLanguage; + private java.nio.file.Path pluginDirectory; + public WebgoatContext(HttpServlet servlet) { this.servlet = servlet; databaseConnectionString = getParameter(servlet, DATABASE_CONNECTION_STRING); @@ -213,4 +217,12 @@ public class WebgoatContext { return defaultLanguage; } + public Path getPluginDirectory() { + return pluginDirectory; + } + + public void setPluginDirectory(Path pluginDirectory) { + this.pluginDirectory = pluginDirectory; + } + } diff --git a/src/main/java/org/owasp/webgoat/util/LabelProvider.java b/src/main/java/org/owasp/webgoat/util/LabelProvider.java index 8126c9eb0..f91ebd3e1 100644 --- a/src/main/java/org/owasp/webgoat/util/LabelProvider.java +++ b/src/main/java/org/owasp/webgoat/util/LabelProvider.java @@ -1,6 +1,7 @@ package org.owasp.webgoat.util; +import org.owasp.webgoat.plugins.ResourceBundleClassLoader; import org.springframework.stereotype.Component; import java.util.HashMap; @@ -38,7 +39,7 @@ import java.util.ResourceBundle; @Component public class LabelProvider { - public final static String DEFAULT_LANGUAGE = "en"; + public final static String DEFAULT_LANGUAGE = Locale.ENGLISH.getLanguage(); private final HashMap labels = new HashMap(); private final WebGoatResourceBundleController localeController = new WebGoatResourceBundleController(); @@ -47,7 +48,8 @@ public class LabelProvider { if (!labels.containsKey(locale)) { - ResourceBundle resBundle = ResourceBundle.getBundle("WebGoatLabels", locale, localeController); + ClassLoader classLoader = ResourceBundleClassLoader.createPropertyFilesClassLoader(ResourceBundle.class.getClassLoader()); + ResourceBundle resBundle = ResourceBundle.getBundle("WebGoatLabels", new Locale(DEFAULT_LANGUAGE), classLoader); labels.put(locale, resBundle); } return labels.get(locale).getString(strName); diff --git a/src/main/resources/WebGoatLabels.properties b/src/main/resources/WebGoatLabels.properties deleted file mode 100644 index 3d9db4fda..000000000 --- a/src/main/resources/WebGoatLabels.properties +++ /dev/null @@ -1,21 +0,0 @@ -#General -LessonCompleted=Congratulations. You have successfully completed this lesson. -RestartLesson=Restart this Lesson -SolutionVideos=Solution Videos -ErrorGenerating=Error generating -InvalidData=Invalid Data -Go!=Go! - - -#StringSqlInjection.java -StringSqlInjectionSecondStage=Now that you have successfully performed an SQL injection, try the same type of attack on a parameterized query. Restart the lesson if you wish to return to the injectable query. -EnterLastName=Enter your last name: -NoResultsMatched=No results matched. Try Again. -SqlStringInjectionHint1=The application is taking your input and inserting it at the end of a pre-formed SQL command. -SqlStringInjectionHint2=This is the code for the query being built and issued by WebGoat:

"SELECT * FROM user_data WHERE last_name = "accountName" -SqlStringInjectionHint3=Compound SQL statements can be made by joining multiple tests with keywords like AND and OR. Try appending a SQL statement that always resolves to true -SqlStringInjectionHint4=Try entering [ smith' OR '1' = '1 ]. - - - - diff --git a/src/main/resources/WebGoatLabels_de.properties b/src/main/resources/WebGoatLabels_de.properties deleted file mode 100644 index d6edeef94..000000000 --- a/src/main/resources/WebGoatLabels_de.properties +++ /dev/null @@ -1,8 +0,0 @@ -#General -LessonCompleted=Herzlichen Gl\u00fcckwunsch! Sie haben diese Lektion erfolgreich abgeschlossen. -RestartLesson=Lektion neu beginnen -SolutionVideos=L\u00f6sungsvideos -ErrorGenerating=Fehler beim Generieren von -InvalidData=Ung\u00fcltige Daten - - \ No newline at end of file diff --git a/src/main/resources/WebGoatLabels_fr.properties b/src/main/resources/WebGoatLabels_fr.properties deleted file mode 100644 index 24b7f4f39..000000000 --- a/src/main/resources/WebGoatLabels_fr.properties +++ /dev/null @@ -1,7 +0,0 @@ -#General -LessonCompleted=F\u00e9licitations. Vous avez termin\u00e9 cette le\u00e7on avec succ\u00e9s. -RestartLesson=Recommencer cette le\u00e7on -SolutionVideos=Solution vid\u00e9os -ErrorGenerating=Error generating -InvalidData=Donn\u00e9e invalide - diff --git a/src/main/resources/WebGoatLabels_ru.properties b/src/main/resources/WebGoatLabels_ru.properties deleted file mode 100644 index 8b7a4132d..000000000 --- a/src/main/resources/WebGoatLabels_ru.properties +++ /dev/null @@ -1,7 +0,0 @@ -#General -LessonCompleted=\u041f\u043e\u0437\u0434\u0440\u0430\u0432\u043b\u044f\u044e. \u0412\u044b \u043f\u043e\u043b\u043d\u043e\u0441\u0442\u044c\u044e \u043f\u0440\u043e\u0448\u043b\u0438 \u0434\u0430\u043d\u043d\u044b\u0439 \u0443\u0440\u043e\u043a. -RestartLesson=\u041d\u0430\u0447\u0430\u043b\u044c \u0441\u043d\u0430\u0447\u0430\u043b\u0430 -SolutionVideos=\u0412\u0438\u0434\u0435\u043e \u0441 \u0440\u0435\u0448\u0435\u043d\u0438\u0435\u043c -ErrorGenerating=\u041f\u0440\u043e\u0438\u0437\u043e\u0448\u043b\u0430 \u043e\u0448\u0438\u0431\u043a\u0430 -InvalidData=\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 - diff --git a/src/main/webapp/js/ace/mode-praat.js b/src/main/webapp/js/ace/mode-praat.js index bbd5618ac..d47e332f4 100644 --- a/src/main/webapp/js/ace/mode-praat.js +++ b/src/main/webapp/js/ace/mode-praat.js @@ -1 +1 @@ -ace.define("ace/mode/praat_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){var e="if|then|else|elsif|elif|endif|fi|endfor|endproc|while|endwhile|repeat|until|select|plus|minus|assert",t="macintosh|windows|unix|praatVersion|praatVersion\\$pi|undefined|newline\\$|tab\\$|shellDirectory\\$|homeDirectory\\$|preferencesDirectory\\$|temporaryDirectory\\$|defaultDirectory\\$",n="clearinfo|endSendPraat",r="writeInfo|writeInfoLine|appendInfo|appendInfoLine|writeFile|writeFileLine|appendFile|appendFileLine|abs|round|floor|ceiling|min|max|imin|imax|sqrt|sin|cos|tan|arcsin|arccos|arctan|arctan2|sinc|sincpi|exp|ln|log10|log2|sinh|cosh|tanh|arcsinh|arccosh|actanh|sigmoid|invSigmoid|erf|erfc|randomUniform|randomInteger|randomGauss|randomPoisson|lnGamma|gaussP|gaussQ|invGaussQ|chiSquareP|chiSquareQ|invChiSquareQ|studentP|studentQ|invStudentQ|fisherP|fisherQ|invFisherQ|binomialP|binomialQ|invBinomialP|invBinomialQ|hertzToBark|barkToHerz|hertzToMel|melToHertz|hertzToSemitones|semitonesToHerz|erb|hertzToErb|erbToHertz|phonToDifferenceLimens|differenceLimensToPhon|beta|besselI|besselK|selected|selected\\$|numberOfSelected|variableExists|index|rindex|startsWith|endsWith|index_regex|rindex_regex|replace_regex\\$|length|extractWord\\$|extractLine\\$|extractNumber|left\\$|right\\$|mid\\$|replace\\$|beginPause|endPause|demoShow|demoWindowTitle|demoInput|demoWaitForInput|demoClicked|demoClickedIn|demoX|demoY|demoKeyPressed|demoKey\\$|demoExtraControlKeyPressed|demoShiftKeyPressed|demoCommandKeyPressed|demoOptionKeyPressed|environment\\$|chooseReadFile\\$|chooseDirectory\\$|createDirectory|fileReadable|deleteFile|selectObject|removeObject|plusObject|minusObject|runScript|exitScript|beginSendPraat|endSendPraat",i="Activation|AffineTransform|AmplitudeTier|Art|Artword|Autosegment|BarkFilter|CCA|Categories|Cepstrum|Cepstrumc|ChebyshevSeries|ClassificationTable|Cochleagram|Collection|Configuration|Confusion|ContingencyTable|Corpus|Correlation|Covariance|CrossCorrelationTable|CrossCorrelationTables|DTW|Diagonalizer|Discriminant|Dissimilarity|Distance|Distributions|DurationTier|EEG|ERP|ERPTier|Eigen|Excitation|Excitations|ExperimentMFC|FFNet|FeatureWeights|Formant|FormantFilter|FormantGrid|FormantPoint|FormantTier|GaussianMixture|HMM|HMM_Observation|HMM_ObservationSequence|HMM_State|HMM_StateSequence|Harmonicity|ISpline|Index|Intensity|IntensityTier|IntervalTier|KNN|KlattGrid|KlattTable|LFCC|LPC|Label|LegendreSeries|LinearRegression|LogisticRegression|LongSound|Ltas|MFCC|MSpline|ManPages|Manipulation|Matrix|MelFilter|MixingMatrix|Movie|Network|OTGrammar|OTHistory|OTMulti|PCA|PairDistribution|ParamCurve|Pattern|Permutation|Pitch|PitchTier|PointProcess|Polygon|Polynomial|Procrustes|RealPoint|RealTier|ResultsMFC|Roots|SPINET|SSCP|SVD|Salience|ScalarProduct|Similarity|SimpleString|SortedSetOfString|Sound|Speaker|Spectrogram|Spectrum|SpectrumTier|SpeechSynthesizer|SpellingChecker|Strings|StringsIndex|Table|TableOfReal|TextGrid|TextInterval|TextPoint|TextTier|Tier|Transition|VocalTract|Weight|WordList";this.$rules={start:[{token:"string.interpolated",regex:/'((?:[a-z][a-zA-Z0-9_]*)(?:\$|#|:[0-9]+)?)'/},{token:["text","text","keyword.operator","text","keyword"],regex:/(^\s*)(?:([a-z][a-zA-Z0-9_]*\$?\s+)(=)(\s+))?(stopwatch)/},{token:["text","keyword","text","string"],regex:/(^\s*)(print(?:line)?|echo|exit|pause|sendpraat|include|execute)(\s+)(.*)/},{token:["text","keyword"],regex:"(^\\s*)("+n+")$"},{token:["text","keyword.operator","text"],regex:/(\s+)((?:\+|-|\/|\*|<|>)=?|==?|!=|%|\^|\||and|or|not)(\s+)/},{token:["text","text","keyword.operator","text","keyword","text","keyword"],regex:/(^\s*)(?:([a-z][a-zA-Z0-9_]*\$?\s+)(=)(\s+))?(?:((?:no)?warn|nocheck|noprogress)(\s+))?((?:[A-Z][^.:"]+)(?:$|(?:\.{3}|:)))/},{token:["text","keyword","text","keyword"],regex:/(^\s*)(?:(demo)?(\s+))((?:[A-Z][^.:"]+)(?:$|(?:\.{3}|:)))/},{token:["text","keyword","text","keyword"],regex:/^(\s*)(?:(demo)(\s+))?(10|12|14|16|24)$/},{token:["text","support.function","text"],regex:/(\s*)(do\$?)(\s*:\s*|\s*\(\s*)/},{token:"entity.name.type",regex:"("+i+")"},{token:"variable.language",regex:"("+t+")"},{token:["support.function","text"],regex:"((?:"+r+")\\$?)(\\s*(?::|\\())"},{token:"keyword",regex:/(\bfor\b)/,next:"for"},{token:"keyword",regex:"(\\b(?:"+e+")\\b)"},{token:"string",regex:/"[^"]*"/},{token:"string",regex:/"[^"]*$/,next:"brokenstring"},{token:["text","keyword","text","entity.name.section"],regex:/(^\s*)(\bform\b)(\s+)(.*)/,next:"form"},{token:"constant.numeric",regex:/\b[+-]?\d+(?:(?:\.\d*)?(?:[eE][+-]?\d+)?)?\b/},{token:["keyword","text","entity.name.function"],regex:/(procedure)(\s+)(\S+)/},{token:["entity.name.function","text"],regex:/(@\S+)(:|\s*\()/},{token:["text","keyword","text","entity.name.function"],regex:/(^\s*)(call)(\s+)(\S+)/},{token:"comment",regex:/(^\s*#|;).*$/},{token:"text",regex:/\s+/}],form:[{token:["keyword","text","constant.numeric"],regex:/((?:optionmenu|choice)\s+)(\S+:\s+)([0-9]+)/},{token:["keyword","constant.numeric"],regex:/((?:option|button)\s+)([+-]?\d+(?:(?:\.\d*)?(?:[eE][+-]?\d+)?)?\b)/},{token:["keyword","string"],regex:/((?:option|button)\s+)(.*)/},{token:["keyword","text","string"],regex:/((?:sentence|text)\s+)(\S+\s*)(.*)/},{token:["keyword","text","string","invalid.illegal"],regex:/(word\s+)(\S+\s*)(\S+)?(\s.*)?/},{token:["keyword","text","constant.language"],regex:/(boolean\s+)(\S+\s*)(0|1|"?(?:yes|no)"?)/},{token:["keyword","text","constant.numeric"],regex:/((?:real|natural|positive|integer)\s+)(\S+\s*)([+-]?\d+(?:(?:\.\d*)?(?:[eE][+-]?\d+)?)?\b)/},{token:["keyword","string"],regex:/(comment\s+)(.*)/},{token:"keyword",regex:"endform",next:"start"}],"for":[{token:["keyword","text","constant.numeric","text"],regex:/(from|to)(\s+)([+-]?\d+(?:(?:\.\d*)?(?:[eE][+-]?\d+)?)?)(\s*)/},{token:["keyword","text"],regex:/(from|to)(\s+\S+\s*)/},{token:"text",regex:/$/,next:"start"}],brokenstring:[{token:["text","string"],regex:/(\s*\.{3})([^"]*)/},{token:"string",regex:/"/,next:"start"}]}};r.inherits(s,i),t.PraatHighlightRules=s}),ace.define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"],function(e,t,n){"use strict";var r=e("../range").Range,i=function(){};(function(){this.checkOutdent=function(e,t){return/^\s+$/.test(e)?/^\s*\}/.test(t):!1},this.autoOutdent=function(e,t){var n=e.getLine(t),i=n.match(/^(\s*\})/);if(!i)return 0;var s=i[1].length,o=e.findMatchingBracket({row:t,column:s});if(!o||o.row==t)return 0;var u=this.$getIndent(e.getLine(o.row));e.replace(new r(t,0,t,s-1),u)},this.$getIndent=function(e){return e.match(/^\s*/)[0]}}).call(i.prototype),t.MatchingBraceOutdent=i}),ace.define("ace/mode/folding/cstyle",["require","exports","module","ace/lib/oop","ace/range","ace/mode/folding/fold_mode"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../../range").Range,s=e("./fold_mode").FoldMode,o=t.FoldMode=function(e){e&&(this.foldingStartMarker=new RegExp(this.foldingStartMarker.source.replace(/\|[^|]*?$/,"|"+e.start)),this.foldingStopMarker=new RegExp(this.foldingStopMarker.source.replace(/\|[^|]*?$/,"|"+e.end)))};r.inherits(o,s),function(){this.foldingStartMarker=/(\{|\[)[^\}\]]*$|^\s*(\/\*)/,this.foldingStopMarker=/^[^\[\{]*(\}|\])|^[\s\*]*(\*\/)/,this.getFoldWidgetRange=function(e,t,n,r){var i=e.getLine(n),s=i.match(this.foldingStartMarker);if(s){var o=s.index;if(s[1])return this.openingBracketBlock(e,s[1],n,o);var u=e.getCommentFoldRange(n,o+s[0].length,1);return u&&!u.isMultiLine()&&(r?u=this.getSectionRange(e,n):t!="all"&&(u=null)),u}if(t==="markbegin")return;var s=i.match(this.foldingStopMarker);if(s){var o=s.index+s[0].length;return s[1]?this.closingBracketBlock(e,s[1],n,o):e.getCommentFoldRange(n,o,-1)}},this.getSectionRange=function(e,t){var n=e.getLine(t),r=n.search(/\S/),s=t,o=n.length;t+=1;var u=t,a=e.getLength();while(++tf)break;var l=this.getFoldWidgetRange(e,"all",t);if(l){if(l.start.row<=s)break;if(l.isMultiLine())t=l.end.row;else if(r==f)break}u=t}return new i(s,o,u,e.getLine(u).length)}}.call(o.prototype)}),ace.define("ace/mode/praat",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/praat_highlight_rules","ace/mode/matching_brace_outdent","ace/range","ace/mode/folding/cstyle"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./praat_highlight_rules").PraatHighlightRules,o=e("./matching_brace_outdent").MatchingBraceOutdent,u=e("../range").Range,a=e("./folding/cstyle").FoldMode,f=function(){this.HighlightRules=s,this.$outdent=new o};r.inherits(f,i),function(){this.lineCommentStart="#",this.getNextLineIndent=function(e,t,n){var r=this.$getIndent(t),i=this.getTokenizer().getLineTokens(t,e),s=i.tokens;if(s.length&&s[s.length-1].type=="comment")return r;if(e=="start"){var o=t.match(/^.*[\{\(\[\:]\s*$/);o&&(r+=n)}return r},this.checkOutdent=function(e,t,n){return this.$outdent.checkOutdent(t,n)},this.autoOutdent=function(e,t,n){this.$outdent.autoOutdent(t,n)},this.$id="ace/mode/praat"}.call(f.prototype),t.Mode=f}) \ No newline at end of file +ace.define("ace/mode/praat_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){var e="if|then|else|elsif|elif|endif|fi|endfor|endproc|while|endwhile|repeat|until|select|plus|minus|assert",t="macintosh|windows|unix|praatVersion|praatVersion\\$pi|undefined|newline\\$|tab\\$|shellDirectory\\$|homeDirectory\\$|preferencesDirectory\\$|temporaryDirectory\\$|defaultDirectory\\$",n="clearinfo|endSendPraat",r="writeInfo|writeInfoLine|appendInfo|appendInfoLine|writeFile|writeFileLine|appendFile|appendFileLine|abs|round|floor|ceiling|min|max|imin|imax|sqrt|sin|cos|tan|arcsin|arccos|arctan|arctan2|sinc|sincpi|exp|ln|log10|log2|sinh|cosh|tanh|arcsinh|arccosh|actanh|sigmoid|invSigmoid|erf|erfc|randomUniform|randomInteger|randomGauss|randomPoisson|lnGamma|gaussP|gaussQ|invGaussQ|chiSquareP|chiSquareQ|invChiSquareQ|studentP|studentQ|invStudentQ|fisherP|fisherQ|invFisherQ|binomialP|binomialQ|invBinomialP|invBinomialQ|hertzToBark|barkToHerz|hertzToMel|melToHertz|hertzToSemitones|semitonesToHerz|erb|hertzToErb|erbToHertz|phonToDifferenceLimens|differenceLimensToPhon|beta|besselI|besselK|selected|selected\\$|numberOfSelected|variableExists|index|rindex|startsWith|fileEndsWith|index_regex|rindex_regex|replace_regex\\$|length|extractWord\\$|extractLine\\$|extractNumber|left\\$|right\\$|mid\\$|replace\\$|beginPause|endPause|demoShow|demoWindowTitle|demoInput|demoWaitForInput|demoClicked|demoClickedIn|demoX|demoY|demoKeyPressed|demoKey\\$|demoExtraControlKeyPressed|demoShiftKeyPressed|demoCommandKeyPressed|demoOptionKeyPressed|environment\\$|chooseReadFile\\$|chooseDirectory\\$|createDirectory|fileReadable|deleteFile|selectObject|removeObject|plusObject|minusObject|runScript|exitScript|beginSendPraat|endSendPraat",i="Activation|AffineTransform|AmplitudeTier|Art|Artword|Autosegment|BarkFilter|CCA|Categories|Cepstrum|Cepstrumc|ChebyshevSeries|ClassificationTable|Cochleagram|Collection|Configuration|Confusion|ContingencyTable|Corpus|Correlation|Covariance|CrossCorrelationTable|CrossCorrelationTables|DTW|Diagonalizer|Discriminant|Dissimilarity|Distance|Distributions|DurationTier|EEG|ERP|ERPTier|Eigen|Excitation|Excitations|ExperimentMFC|FFNet|FeatureWeights|Formant|FormantFilter|FormantGrid|FormantPoint|FormantTier|GaussianMixture|HMM|HMM_Observation|HMM_ObservationSequence|HMM_State|HMM_StateSequence|Harmonicity|ISpline|Index|Intensity|IntensityTier|IntervalTier|KNN|KlattGrid|KlattTable|LFCC|LPC|Label|LegendreSeries|LinearRegression|LogisticRegression|LongSound|Ltas|MFCC|MSpline|ManPages|Manipulation|Matrix|MelFilter|MixingMatrix|Movie|Network|OTGrammar|OTHistory|OTMulti|PCA|PairDistribution|ParamCurve|Pattern|Permutation|Pitch|PitchTier|PointProcess|Polygon|Polynomial|Procrustes|RealPoint|RealTier|ResultsMFC|Roots|SPINET|SSCP|SVD|Salience|ScalarProduct|Similarity|SimpleString|SortedSetOfString|Sound|Speaker|Spectrogram|Spectrum|SpectrumTier|SpeechSynthesizer|SpellingChecker|Strings|StringsIndex|Table|TableOfReal|TextGrid|TextInterval|TextPoint|TextTier|Tier|Transition|VocalTract|Weight|WordList";this.$rules={start:[{token:"string.interpolated",regex:/'((?:[a-z][a-zA-Z0-9_]*)(?:\$|#|:[0-9]+)?)'/},{token:["text","text","keyword.operator","text","keyword"],regex:/(^\s*)(?:([a-z][a-zA-Z0-9_]*\$?\s+)(=)(\s+))?(stopwatch)/},{token:["text","keyword","text","string"],regex:/(^\s*)(print(?:line)?|echo|exit|pause|sendpraat|include|execute)(\s+)(.*)/},{token:["text","keyword"],regex:"(^\\s*)("+n+")$"},{token:["text","keyword.operator","text"],regex:/(\s+)((?:\+|-|\/|\*|<|>)=?|==?|!=|%|\^|\||and|or|not)(\s+)/},{token:["text","text","keyword.operator","text","keyword","text","keyword"],regex:/(^\s*)(?:([a-z][a-zA-Z0-9_]*\$?\s+)(=)(\s+))?(?:((?:no)?warn|nocheck|noprogress)(\s+))?((?:[A-Z][^.:"]+)(?:$|(?:\.{3}|:)))/},{token:["text","keyword","text","keyword"],regex:/(^\s*)(?:(demo)?(\s+))((?:[A-Z][^.:"]+)(?:$|(?:\.{3}|:)))/},{token:["text","keyword","text","keyword"],regex:/^(\s*)(?:(demo)(\s+))?(10|12|14|16|24)$/},{token:["text","support.function","text"],regex:/(\s*)(do\$?)(\s*:\s*|\s*\(\s*)/},{token:"entity.name.type",regex:"("+i+")"},{token:"variable.language",regex:"("+t+")"},{token:["support.function","text"],regex:"((?:"+r+")\\$?)(\\s*(?::|\\())"},{token:"keyword",regex:/(\bfor\b)/,next:"for"},{token:"keyword",regex:"(\\b(?:"+e+")\\b)"},{token:"string",regex:/"[^"]*"/},{token:"string",regex:/"[^"]*$/,next:"brokenstring"},{token:["text","keyword","text","entity.name.section"],regex:/(^\s*)(\bform\b)(\s+)(.*)/,next:"form"},{token:"constant.numeric",regex:/\b[+-]?\d+(?:(?:\.\d*)?(?:[eE][+-]?\d+)?)?\b/},{token:["keyword","text","entity.name.function"],regex:/(procedure)(\s+)(\S+)/},{token:["entity.name.function","text"],regex:/(@\S+)(:|\s*\()/},{token:["text","keyword","text","entity.name.function"],regex:/(^\s*)(call)(\s+)(\S+)/},{token:"comment",regex:/(^\s*#|;).*$/},{token:"text",regex:/\s+/}],form:[{token:["keyword","text","constant.numeric"],regex:/((?:optionmenu|choice)\s+)(\S+:\s+)([0-9]+)/},{token:["keyword","constant.numeric"],regex:/((?:option|button)\s+)([+-]?\d+(?:(?:\.\d*)?(?:[eE][+-]?\d+)?)?\b)/},{token:["keyword","string"],regex:/((?:option|button)\s+)(.*)/},{token:["keyword","text","string"],regex:/((?:sentence|text)\s+)(\S+\s*)(.*)/},{token:["keyword","text","string","invalid.illegal"],regex:/(word\s+)(\S+\s*)(\S+)?(\s.*)?/},{token:["keyword","text","constant.language"],regex:/(boolean\s+)(\S+\s*)(0|1|"?(?:yes|no)"?)/},{token:["keyword","text","constant.numeric"],regex:/((?:real|natural|positive|integer)\s+)(\S+\s*)([+-]?\d+(?:(?:\.\d*)?(?:[eE][+-]?\d+)?)?\b)/},{token:["keyword","string"],regex:/(comment\s+)(.*)/},{token:"keyword",regex:"endform",next:"start"}],"for":[{token:["keyword","text","constant.numeric","text"],regex:/(from|to)(\s+)([+-]?\d+(?:(?:\.\d*)?(?:[eE][+-]?\d+)?)?)(\s*)/},{token:["keyword","text"],regex:/(from|to)(\s+\S+\s*)/},{token:"text",regex:/$/,next:"start"}],brokenstring:[{token:["text","string"],regex:/(\s*\.{3})([^"]*)/},{token:"string",regex:/"/,next:"start"}]}};r.inherits(s,i),t.PraatHighlightRules=s}),ace.define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"],function(e,t,n){"use strict";var r=e("../range").Range,i=function(){};(function(){this.checkOutdent=function(e,t){return/^\s+$/.test(e)?/^\s*\}/.test(t):!1},this.autoOutdent=function(e,t){var n=e.getLine(t),i=n.match(/^(\s*\})/);if(!i)return 0;var s=i[1].length,o=e.findMatchingBracket({row:t,column:s});if(!o||o.row==t)return 0;var u=this.$getIndent(e.getLine(o.row));e.replace(new r(t,0,t,s-1),u)},this.$getIndent=function(e){return e.match(/^\s*/)[0]}}).call(i.prototype),t.MatchingBraceOutdent=i}),ace.define("ace/mode/folding/cstyle",["require","exports","module","ace/lib/oop","ace/range","ace/mode/folding/fold_mode"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../../range").Range,s=e("./fold_mode").FoldMode,o=t.FoldMode=function(e){e&&(this.foldingStartMarker=new RegExp(this.foldingStartMarker.source.replace(/\|[^|]*?$/,"|"+e.start)),this.foldingStopMarker=new RegExp(this.foldingStopMarker.source.replace(/\|[^|]*?$/,"|"+e.end)))};r.inherits(o,s),function(){this.foldingStartMarker=/(\{|\[)[^\}\]]*$|^\s*(\/\*)/,this.foldingStopMarker=/^[^\[\{]*(\}|\])|^[\s\*]*(\*\/)/,this.getFoldWidgetRange=function(e,t,n,r){var i=e.getLine(n),s=i.match(this.foldingStartMarker);if(s){var o=s.index;if(s[1])return this.openingBracketBlock(e,s[1],n,o);var u=e.getCommentFoldRange(n,o+s[0].length,1);return u&&!u.isMultiLine()&&(r?u=this.getSectionRange(e,n):t!="all"&&(u=null)),u}if(t==="markbegin")return;var s=i.match(this.foldingStopMarker);if(s){var o=s.index+s[0].length;return s[1]?this.closingBracketBlock(e,s[1],n,o):e.getCommentFoldRange(n,o,-1)}},this.getSectionRange=function(e,t){var n=e.getLine(t),r=n.search(/\S/),s=t,o=n.length;t+=1;var u=t,a=e.getLength();while(++tf)break;var l=this.getFoldWidgetRange(e,"all",t);if(l){if(l.start.row<=s)break;if(l.isMultiLine())t=l.end.row;else if(r==f)break}u=t}return new i(s,o,u,e.getLine(u).length)}}.call(o.prototype)}),ace.define("ace/mode/praat",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/praat_highlight_rules","ace/mode/matching_brace_outdent","ace/range","ace/mode/folding/cstyle"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./praat_highlight_rules").PraatHighlightRules,o=e("./matching_brace_outdent").MatchingBraceOutdent,u=e("../range").Range,a=e("./folding/cstyle").FoldMode,f=function(){this.HighlightRules=s,this.$outdent=new o};r.inherits(f,i),function(){this.lineCommentStart="#",this.getNextLineIndent=function(e,t,n){var r=this.$getIndent(t),i=this.getTokenizer().getLineTokens(t,e),s=i.tokens;if(s.length&&s[s.length-1].type=="comment")return r;if(e=="start"){var o=t.match(/^.*[\{\(\[\:]\s*$/);o&&(r+=n)}return r},this.checkOutdent=function(e,t,n){return this.$outdent.checkOutdent(t,n)},this.autoOutdent=function(e,t,n){this.$outdent.autoOutdent(t,n)},this.$id="ace/mode/praat"}.call(f.prototype),t.Mode=f}) \ No newline at end of file diff --git a/src/main/webapp/js/ace/snippets/jsp.js b/src/main/webapp/js/ace/snippets/jsp.js index fcbace719..c39e6c084 100644 --- a/src/main/webapp/js/ace/snippets/jsp.js +++ b/src/main/webapp/js/ace/snippets/jsp.js @@ -1 +1 @@ -ace.define("ace/snippets/jsp",["require","exports","module"],function(e,t,n){"use strict";t.snippetText='snippet @page\n <%@page contentType="text/html" pageEncoding="UTF-8"%>\nsnippet jstl\n <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>\n <%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>\nsnippet jstl:c\n <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>\nsnippet jstl:fn\n <%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>\nsnippet cpath\n ${pageContext.request.contextPath}\nsnippet cout\n \nsnippet cset\n \nsnippet cremove\n \nsnippet ccatch\n \nsnippet cif\n \n ${2}\n \nsnippet cchoose\n \n ${1}\n \nsnippet cwhen\n \n ${2}\n \nsnippet cother\n \n ${1}\n \nsnippet cfore\n \n ${4:}\n \nsnippet cfort\n ${2:item1,item2,item3}\n \n ${5:}\n \nsnippet cparam\n \nsnippet cparam+\n \n cparam+${3}\nsnippet cimport\n \nsnippet cimport+\n \n \n cparam+${4}\n \nsnippet curl\n \n ${3}\nsnippet curl+\n \n \n cparam+${6}\n \n ${3}\nsnippet credirect\n \nsnippet contains\n ${fn:contains(${1:string}, ${2:substr})}\nsnippet contains:i\n ${fn:containsIgnoreCase(${1:string}, ${2:substr})}\nsnippet endswith\n ${fn:endsWith(${1:string}, ${2:suffix})}\nsnippet escape\n ${fn:escapeXml(${1:string})}\nsnippet indexof\n ${fn:indexOf(${1:string}, ${2:substr})}\nsnippet join\n ${fn:join(${1:collection}, ${2:delims})}\nsnippet length\n ${fn:length(${1:collection_or_string})}\nsnippet replace\n ${fn:replace(${1:string}, ${2:substr}, ${3:replace})}\nsnippet split\n ${fn:split(${1:string}, ${2:delims})}\nsnippet startswith\n ${fn:startsWith(${1:string}, ${2:prefix})}\nsnippet substr\n ${fn:substring(${1:string}, ${2:begin}, ${3:end})}\nsnippet substr:a\n ${fn:substringAfter(${1:string}, ${2:substr})}\nsnippet substr:b\n ${fn:substringBefore(${1:string}, ${2:substr})}\nsnippet lc\n ${fn:toLowerCase(${1:string})}\nsnippet uc\n ${fn:toUpperCase(${1:string})}\nsnippet trim\n ${fn:trim(${1:string})}\n',t.scope="jsp"}) \ No newline at end of file +ace.define("ace/snippets/jsp",["require","exports","module"],function(e,t,n){"use strict";t.snippetText='snippet @page\n <%@page contentType="text/html" pageEncoding="UTF-8"%>\nsnippet jstl\n <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>\n <%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>\nsnippet jstl:c\n <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>\nsnippet jstl:fn\n <%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>\nsnippet cpath\n ${pageContext.request.contextPath}\nsnippet cout\n \nsnippet cset\n \nsnippet cremove\n \nsnippet ccatch\n \nsnippet cif\n \n ${2}\n \nsnippet cchoose\n \n ${1}\n \nsnippet cwhen\n \n ${2}\n \nsnippet cother\n \n ${1}\n \nsnippet cfore\n \n ${4:}\n \nsnippet cfort\n ${2:item1,item2,item3}\n \n ${5:}\n \nsnippet cparam\n \nsnippet cparam+\n \n cparam+${3}\nsnippet cimport\n \nsnippet cimport+\n \n \n cparam+${4}\n \nsnippet curl\n \n ${3}\nsnippet curl+\n \n \n cparam+${6}\n \n ${3}\nsnippet credirect\n \nsnippet contains\n ${fn:contains(${1:string}, ${2:substr})}\nsnippet contains:i\n ${fn:containsIgnoreCase(${1:string}, ${2:substr})}\nsnippet endswith\n ${fn:fileEndsWith(${1:string}, ${2:suffix})}\nsnippet escape\n ${fn:escapeXml(${1:string})}\nsnippet indexof\n ${fn:indexOf(${1:string}, ${2:substr})}\nsnippet join\n ${fn:join(${1:collection}, ${2:delims})}\nsnippet length\n ${fn:length(${1:collection_or_string})}\nsnippet replace\n ${fn:replace(${1:string}, ${2:substr}, ${3:replace})}\nsnippet split\n ${fn:split(${1:string}, ${2:delims})}\nsnippet startswith\n ${fn:startsWith(${1:string}, ${2:prefix})}\nsnippet substr\n ${fn:substring(${1:string}, ${2:begin}, ${3:end})}\nsnippet substr:a\n ${fn:substringAfter(${1:string}, ${2:substr})}\nsnippet substr:b\n ${fn:substringBefore(${1:string}, ${2:substr})}\nsnippet lc\n ${fn:toLowerCase(${1:string})}\nsnippet uc\n ${fn:toUpperCase(${1:string})}\nsnippet trim\n ${fn:trim(${1:string})}\n',t.scope="jsp"}) \ No newline at end of file