-/*
- * Copyright (C) 2001, 2002 The Mir-coders group
- *
- * This file is part of Mir.
- *
- * Mir is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * Mir is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Mir; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- * In addition, as a special exception, The Mir-coders gives permission to link
- * the code of this program with any library licensed under the Apache Software License,
- * The Sun (tm) Java Advanced Imaging library (JAI), The Sun JIMI library
- * (or with modified versions of the above that use the same license as the above),
- * and distribute linked combinations including the two. You must obey the
- * GNU General Public License in all respects for all of the code used other than
- * the above mentioned libraries. If you modify this file, you may extend this
- * exception to your version of the file, but you are not obligated to do so.
- * If you do not wish to do so, delete this exception statement from your version.
- */
-package mir.generator;
-
-import java.io.PrintWriter;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Vector;
-
-import mir.log.LoggerWrapper;
-import mir.util.*;
-
-import org.apache.commons.beanutils.*;
-
-import freemarker.template.FileTemplateCache;
-import freemarker.template.SimpleScalar;
-import freemarker.template.Template;
-import freemarker.template.TemplateHashModel;
-import freemarker.template.TemplateListModel;
-import freemarker.template.TemplateMethodModel;
-import freemarker.template.TemplateModel;
-import freemarker.template.TemplateModelException;
-import freemarker.template.TemplateModelRoot;
-import freemarker.template.TemplateScalarModel;
-
-
-public class FreemarkerGenerator implements Generator {
- private Template template;
-
- public FreemarkerGenerator(Template aTemplate) {
- template = aTemplate;
- }
-
- public void generate(Object anOutputWriter, Map aValues, LoggerWrapper aLogger) throws GeneratorExc, GeneratorFailure {
- if (!(anOutputWriter instanceof PrintWriter))
- throw new GeneratorExc("Writer for a FreemarkerGenerator must be a PrintWriter");
-
- try {
- template.process((TemplateModelRoot) makeMapAdapter(aValues), (PrintWriter) anOutputWriter);
- }
- catch (Throwable t) {
- t.printStackTrace();
- aLogger.error("Exception occurred: "+t.getMessage());
- t.printStackTrace(aLogger.asPrintWriter(LoggerWrapper.DEBUG_MESSAGE));
- throw new GeneratorFailure( t );
- }
- }
-
- private static TemplateScalarModel makeStringAdapter(String aString) {
- return new SimpleScalar(aString);
- }
-
- private static TemplateHashModel makeMapAdapter(Map aMap) {
- return new MapAdapter(aMap);
- }
-
- private static TemplateListModel makeIteratorAdapter(Iterator anIterator) {
- return new IteratorAdapter(anIterator);
- }
-
- private static TemplateMethodModel makeFunctionAdapter(Generator.GeneratorFunction aFunction) {
- return new FunctionAdapter(aFunction);
- }
-
- private static TemplateHashModel makeBeanAdapter(Object anObject) {
- return new BeanAdapter(anObject);
- }
-
- public static TemplateModel makeAdapter(Object anObject) throws TemplateModelException {
- if (anObject == null)
- return null;
-
- if (anObject instanceof TemplateModel)
- return (TemplateModel) anObject;
- else if (anObject instanceof Generator.GeneratorFunction)
- return makeFunctionAdapter((Generator.GeneratorFunction) anObject);
- else if (anObject instanceof Integer)
- return makeStringAdapter(((Integer) anObject).toString());
- else if (anObject instanceof Boolean) {
- if (((Boolean) anObject).booleanValue())
- return makeStringAdapter("1");
- else
- return makeStringAdapter("0");
- }
- else if (anObject instanceof String)
- return makeStringAdapter((String) anObject);
- else if (anObject instanceof Map)
- return makeMapAdapter((Map) anObject);
- else if (anObject instanceof Iterator)
- return makeIteratorAdapter((Iterator) anObject);
- else if (anObject instanceof List)
- return makeIteratorAdapter(((List) anObject).iterator());
- else if (anObject instanceof Number)
- return makeAdapter(new GeneratorFormatAdapters.NumberFormatAdapter((Number) anObject));
- else
- return makeBeanAdapter(anObject);
- }
-
- private static class MapAdapter implements TemplateModelRoot {
- private Map map;
- private Map valuesCache;
-
- private MapAdapter(Map aMap) {
- map = aMap;
- valuesCache = new HashMap();
- }
-
- public void put(String aKey, TemplateModel aModel) {
- valuesCache.put(aKey, aModel);
- }
-
- public void remove(String aKey) {
- // ML: kinda tricky...
- }
-
- public boolean isEmpty() {
- return map.isEmpty();
- }
-
- public TemplateModel get(String aKey) throws TemplateModelException {
- try {
- if (!valuesCache.containsKey(aKey)) {
- Object value = map.get(aKey);
-
- if (value == null && !map.containsKey(aKey)) {
- throw new TemplateModelException("MapAdapter: no key "+aKey+" available");
- }
-
- valuesCache.put(aKey, makeAdapter(value));
- }
-
- return (TemplateModel) valuesCache.get(aKey);
- }
- catch (TemplateModelException e) {
- throw e;
- }
- catch (Throwable t) {
- throw new TemplateModelException(t.getMessage());
- }
- }
- }
-
- private static class IteratorAdapter implements TemplateListModel {
- private Iterator iterator;
- private List valuesCache;
- private int position;
-
- private IteratorAdapter(Iterator anIterator) {
- iterator = anIterator;
-
- valuesCache = new Vector();
- position=0;
-
-
- if (iterator instanceof RewindableIterator) {
- ((RewindableIterator) iterator).rewind();
- }
- }
-
- public boolean isEmpty() {
- return valuesCache.isEmpty() && !iterator.hasNext();
- }
-
- private void getUntil(int anIndex) throws TemplateModelException {
- while (valuesCache.size()<=anIndex && iterator.hasNext())
- {
- valuesCache.add(makeAdapter(iterator.next()));
- }
- };
-
- public TemplateModel get(int anIndex) throws TemplateModelException {
- TemplateModel result;
-
- getUntil(anIndex);
-
- if (anIndex<valuesCache.size())
- {
- result = (TemplateModel) valuesCache.get(anIndex);
-
- return result;
- }
- else
- throw new TemplateModelException( "Iterator out of bounds" );
- }
-
- public boolean hasNext() {
- return position<valuesCache.size() || iterator.hasNext();
- }
-
- public boolean isRewound() {
- return position==0;
- }
-
- public TemplateModel next() throws TemplateModelException {
- TemplateModel result;
-
- if (hasNext()) {
- result = get(position);
- position++;
- }
- else
- throw new TemplateModelException( "Iterator out of bounds" );
-
- return result;
- }
-
- public void rewind() {
- position=0;
- }
- }
-
- private static class ListAdapter implements TemplateListModel {
- List list;
- List valuesCache;
- int position;
-
- private ListAdapter(List aList) {
- list = aList;
- valuesCache = new Vector();
- position=0;
- }
-
- public boolean isEmpty() {
- return list.isEmpty();
- }
-
- public TemplateModel get(int i) throws TemplateModelException {
-
- if (i>=valuesCache.size() && i<list.size()) {
- for(int j=valuesCache.size(); j<=i; j++) {
- valuesCache.add(makeAdapter(list.get(j)));
- }
- }
-
- if (i<valuesCache.size())
- return (TemplateModel) valuesCache.get(i);
- else
- throw new TemplateModelException( "Iterator out of bounds" );
- }
-
- public boolean hasNext() {
- return position<list.size();
- }
-
- public boolean isRewound() {
- return position==0;
- }
-
- public TemplateModel next() throws TemplateModelException {
- TemplateModel result;
-
- if (hasNext()) {
- result = get(position);
- position++;
- }
- else {
- throw new TemplateModelException( "Iterator out of bounds" );
- }
-
- return result;
- }
-
- public void rewind() {
- position = 0;
- }
- }
-
- private static class FunctionAdapter implements TemplateMethodModel {
- private Generator.GeneratorFunction function;
-
- public FunctionAdapter(Generator.GeneratorFunction aFunction) {
- function = aFunction;
- }
-
- public TemplateModel exec(List anArguments) throws TemplateModelException {
- try {
- return makeAdapter(function.perform(anArguments));
- }
- catch (Throwable t) {
- throw new TemplateModelException(t.getMessage());
- }
- }
-
- public boolean isEmpty() {
- return false;
- }
-
- }
-
- private static class BeanAdapter implements TemplateHashModel {
- private Object object;
-
- public BeanAdapter(Object anObject) {
- object = anObject;
- }
-
- public void put(String aKey, TemplateModel aModel) throws TemplateModelException {
- throw new TemplateModelException("FreemarkerGenerator$BeanAdapter.put not supported");
- }
-
- public void remove(String aKey) throws TemplateModelException {
- throw new TemplateModelException("FreemarkerGenerator$BeanAdapter.remove not supported");
- }
-
- public boolean isEmpty() {
- return false;
- }
-
- public TemplateModel get(String aKey) throws TemplateModelException {
- try {
- if (PropertyUtils.isReadable(object, aKey))
- return makeAdapter(PropertyUtils.getSimpleProperty(object, aKey));
- else
- return makeAdapter(MethodUtils.invokeExactMethod(object, "get", aKey));
- }
- catch (Throwable t) {
- throw new TemplateModelException(t.getMessage());
- }
- }
- }
-
- public static class FreemarkerGeneratorLibrary implements GeneratorLibrary {
- private FileTemplateCache templateCache;
-
- public FreemarkerGeneratorLibrary(String aTemplateRoot) {
- templateCache = new FileTemplateCache( aTemplateRoot+"/" );
- templateCache.setLoadingPolicy(FileTemplateCache.LOAD_ON_DEMAND);
- }
-
- public Generator makeGenerator(String anIdentifier) throws GeneratorExc, GeneratorFailure {
- Template template = (Template) templateCache.getItem(anIdentifier, "template");
-
- if (template==null) {
- throw new GeneratorExc("FreemarkerGeneratorLibrary: Can't find template "+templateCache.getDirectory()+anIdentifier);
- }
-
- return new FreemarkerGenerator(template);
- }
- }
-
- public static class FreemarkerGeneratorLibraryFactory implements GeneratorLibraryFactory {
- private String basePath;
-
- public FreemarkerGeneratorLibraryFactory(String aBasePath) {
- basePath = aBasePath;
- }
-
- public GeneratorLibrary makeLibrary(String anInitializationString) {
- return new FreemarkerGeneratorLibrary(basePath+anInitializationString);
- };
- }
-}
+/*\r
+ * Copyright (C) 2001, 2002 The Mir-coders group\r
+ *\r
+ * This file is part of Mir.\r
+ *\r
+ * Mir is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * Mir is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License\r
+ * along with Mir; if not, write to the Free Software\r
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
+ *\r
+ * In addition, as a special exception, The Mir-coders gives permission to link\r
+ * the code of this program with any library licensed under the Apache Software License,\r
+ * The Sun (tm) Java Advanced Imaging library (JAI), The Sun JIMI library\r
+ * (or with modified versions of the above that use the same license as the above),\r
+ * and distribute linked combinations including the two. You must obey the\r
+ * GNU General Public License in all respects for all of the code used other than\r
+ * the above mentioned libraries. If you modify this file, you may extend this\r
+ * exception to your version of the file, but you are not obligated to do so.\r
+ * If you do not wish to do so, delete this exception statement from your version.\r
+ */\r
+package mir.generator;\r
+\r
+import java.io.PrintWriter;\r
+import java.util.HashMap;\r
+import java.util.Iterator;\r
+import java.util.List;\r
+import java.util.Map;\r
+import java.util.Vector;\r
+\r
+import org.apache.commons.beanutils.MethodUtils;\r
+import org.apache.commons.beanutils.PropertyUtils;\r
+\r
+import freemarker.template.FileTemplateCache;\r
+import freemarker.template.SimpleScalar;\r
+import freemarker.template.Template;\r
+import freemarker.template.TemplateHashModel;\r
+import freemarker.template.TemplateListModel;\r
+import freemarker.template.TemplateMethodModel;\r
+import freemarker.template.TemplateModel;\r
+import freemarker.template.TemplateModelException;\r
+import freemarker.template.TemplateModelRoot;\r
+import freemarker.template.TemplateScalarModel;\r
+\r
+import mir.log.LoggerWrapper;\r
+import mir.util.GeneratorFormatAdapters;\r
+import mir.util.RewindableIterator;\r
+\r
+\r
+public class FreemarkerGenerator implements Generator {\r
+ private Template template;\r
+\r
+ public FreemarkerGenerator(Template aTemplate) {\r
+ template = aTemplate;\r
+ }\r
+\r
+ public void generate(Object anOutputWriter, Map aValues, LoggerWrapper aLogger) throws GeneratorExc, GeneratorFailure {\r
+ if (!(anOutputWriter instanceof PrintWriter))\r
+ throw new GeneratorExc("Writer for a FreemarkerGenerator must be a PrintWriter");\r
+\r
+ try {\r
+ template.process((TemplateModelRoot) makeMapAdapter(aValues), (PrintWriter) anOutputWriter);\r
+ }\r
+ catch (Throwable t) {\r
+ t.printStackTrace();\r
+ aLogger.error("Exception occurred: "+t.getMessage());\r
+ t.printStackTrace(aLogger.asPrintWriter(LoggerWrapper.DEBUG_MESSAGE));\r
+ throw new GeneratorFailure( t );\r
+ }\r
+ }\r
+\r
+ private static TemplateScalarModel makeStringAdapter(String aString) {\r
+ return new SimpleScalar(aString);\r
+ }\r
+\r
+ private static TemplateHashModel makeMapAdapter(Map aMap) {\r
+ return new MapAdapter(aMap);\r
+ }\r
+\r
+ private static TemplateListModel makeIteratorAdapter(Iterator anIterator) {\r
+ return new IteratorAdapter(anIterator);\r
+ }\r
+\r
+ private static TemplateMethodModel makeFunctionAdapter(Generator.GeneratorFunction aFunction) {\r
+ return new FunctionAdapter(aFunction);\r
+ }\r
+\r
+ private static TemplateHashModel makeBeanAdapter(Object anObject) {\r
+ return new BeanAdapter(anObject);\r
+ }\r
+\r
+ public static TemplateModel makeAdapter(Object anObject) throws TemplateModelException {\r
+ if (anObject == null)\r
+ return null;\r
+\r
+ if (anObject instanceof TemplateModel)\r
+ return (TemplateModel) anObject;\r
+ else if (anObject instanceof Generator.GeneratorFunction)\r
+ return makeFunctionAdapter((Generator.GeneratorFunction) anObject);\r
+ else if (anObject instanceof Integer)\r
+ return makeStringAdapter(((Integer) anObject).toString());\r
+ else if (anObject instanceof Boolean) {\r
+ if (((Boolean) anObject).booleanValue())\r
+ return makeStringAdapter("1");\r
+ else\r
+ return makeStringAdapter("0");\r
+ }\r
+ else if (anObject instanceof String)\r
+ return makeStringAdapter((String) anObject);\r
+ else if (anObject instanceof Map)\r
+ return makeMapAdapter((Map) anObject);\r
+ else if (anObject instanceof Iterator)\r
+ return makeIteratorAdapter((Iterator) anObject);\r
+ else if (anObject instanceof List)\r
+ return makeIteratorAdapter(((List) anObject).iterator());\r
+ else if (anObject instanceof Number)\r
+ return makeAdapter(new GeneratorFormatAdapters.NumberFormatAdapter((Number) anObject));\r
+ else\r
+ return makeBeanAdapter(anObject);\r
+ }\r
+\r
+ private static class MapAdapter implements TemplateModelRoot {\r
+ private Map map;\r
+ private Map valuesCache;\r
+\r
+ private MapAdapter(Map aMap) {\r
+ map = aMap;\r
+ valuesCache = new HashMap();\r
+ }\r
+\r
+ public void put(String aKey, TemplateModel aModel) {\r
+ valuesCache.put(aKey, aModel);\r
+ }\r
+\r
+ public void remove(String aKey) {\r
+ // ML: kinda tricky...\r
+ }\r
+\r
+ public boolean isEmpty() {\r
+ return map.isEmpty();\r
+ }\r
+\r
+ public TemplateModel get(String aKey) throws TemplateModelException {\r
+ try {\r
+ if (!valuesCache.containsKey(aKey)) {\r
+ Object value = map.get(aKey);\r
+\r
+ if (value == null && !map.containsKey(aKey)) {\r
+ throw new TemplateModelException("MapAdapter: no key "+aKey+" available");\r
+ }\r
+\r
+ valuesCache.put(aKey, makeAdapter(value));\r
+ }\r
+\r
+ return (TemplateModel) valuesCache.get(aKey);\r
+ }\r
+ catch (TemplateModelException e) {\r
+ throw e;\r
+ }\r
+ catch (Throwable t) {\r
+ throw new TemplateModelException(t.getMessage());\r
+ }\r
+ }\r
+ }\r
+\r
+ private static class IteratorAdapter implements TemplateListModel {\r
+ private Iterator iterator;\r
+ private List valuesCache;\r
+ private int position;\r
+\r
+ private IteratorAdapter(Iterator anIterator) {\r
+ iterator = anIterator;\r
+\r
+ valuesCache = new Vector();\r
+ position=0;\r
+\r
+\r
+ if (iterator instanceof RewindableIterator) {\r
+ ((RewindableIterator) iterator).rewind();\r
+ }\r
+ }\r
+\r
+ public boolean isEmpty() {\r
+ return valuesCache.isEmpty() && !iterator.hasNext();\r
+ }\r
+\r
+ private void getUntil(int anIndex) throws TemplateModelException {\r
+ while (valuesCache.size()<=anIndex && iterator.hasNext())\r
+ {\r
+ valuesCache.add(makeAdapter(iterator.next()));\r
+ }\r
+ };\r
+\r
+ public TemplateModel get(int anIndex) throws TemplateModelException {\r
+ TemplateModel result;\r
+\r
+ getUntil(anIndex);\r
+\r
+ if (anIndex<valuesCache.size())\r
+ {\r
+ result = (TemplateModel) valuesCache.get(anIndex);\r
+\r
+ return result;\r
+ }\r
+ else\r
+ throw new TemplateModelException( "Iterator out of bounds" );\r
+ }\r
+\r
+ public boolean hasNext() {\r
+ return position<valuesCache.size() || iterator.hasNext();\r
+ }\r
+\r
+ public boolean isRewound() {\r
+ return position==0;\r
+ }\r
+\r
+ public TemplateModel next() throws TemplateModelException {\r
+ TemplateModel result;\r
+\r
+ if (hasNext()) {\r
+ result = get(position);\r
+ position++;\r
+ }\r
+ else\r
+ throw new TemplateModelException( "Iterator out of bounds" );\r
+\r
+ return result;\r
+ }\r
+\r
+ public void rewind() {\r
+ position=0;\r
+ }\r
+ }\r
+\r
+ private static class ListAdapter implements TemplateListModel {\r
+ List list;\r
+ List valuesCache;\r
+ int position;\r
+\r
+ private ListAdapter(List aList) {\r
+ list = aList;\r
+ valuesCache = new Vector();\r
+ position=0;\r
+ }\r
+\r
+ public boolean isEmpty() {\r
+ return list.isEmpty();\r
+ }\r
+\r
+ public TemplateModel get(int i) throws TemplateModelException {\r
+\r
+ if (i>=valuesCache.size() && i<list.size()) {\r
+ for(int j=valuesCache.size(); j<=i; j++) {\r
+ valuesCache.add(makeAdapter(list.get(j)));\r
+ }\r
+ }\r
+\r
+ if (i<valuesCache.size())\r
+ return (TemplateModel) valuesCache.get(i);\r
+ else\r
+ throw new TemplateModelException( "Iterator out of bounds" );\r
+ }\r
+\r
+ public boolean hasNext() {\r
+ return position<list.size();\r
+ }\r
+\r
+ public boolean isRewound() {\r
+ return position==0;\r
+ }\r
+\r
+ public TemplateModel next() throws TemplateModelException {\r
+ TemplateModel result;\r
+\r
+ if (hasNext()) {\r
+ result = get(position);\r
+ position++;\r
+ }\r
+ else {\r
+ throw new TemplateModelException( "Iterator out of bounds" );\r
+ }\r
+\r
+ return result;\r
+ }\r
+\r
+ public void rewind() {\r
+ position = 0;\r
+ }\r
+ }\r
+\r
+ private static class FunctionAdapter implements TemplateMethodModel {\r
+ private Generator.GeneratorFunction function;\r
+\r
+ public FunctionAdapter(Generator.GeneratorFunction aFunction) {\r
+ function = aFunction;\r
+ }\r
+\r
+ public TemplateModel exec(List anArguments) throws TemplateModelException {\r
+ try {\r
+ return makeAdapter(function.perform(anArguments));\r
+ }\r
+ catch (Throwable t) {\r
+ throw new TemplateModelException(t.getMessage());\r
+ }\r
+ }\r
+\r
+ public boolean isEmpty() {\r
+ return false;\r
+ }\r
+\r
+ }\r
+\r
+ private static class BeanAdapter implements TemplateHashModel {\r
+ private Object object;\r
+\r
+ public BeanAdapter(Object anObject) {\r
+ object = anObject;\r
+ }\r
+\r
+ public void put(String aKey, TemplateModel aModel) throws TemplateModelException {\r
+ throw new TemplateModelException("FreemarkerGenerator$BeanAdapter.put not supported");\r
+ }\r
+\r
+ public void remove(String aKey) throws TemplateModelException {\r
+ throw new TemplateModelException("FreemarkerGenerator$BeanAdapter.remove not supported");\r
+ }\r
+\r
+ public boolean isEmpty() {\r
+ return false;\r
+ }\r
+\r
+ public TemplateModel get(String aKey) throws TemplateModelException {\r
+ try {\r
+ if (PropertyUtils.isReadable(object, aKey))\r
+ return makeAdapter(PropertyUtils.getSimpleProperty(object, aKey));\r
+ else\r
+ return makeAdapter(MethodUtils.invokeExactMethod(object, "get", aKey));\r
+ }\r
+ catch (Throwable t) {\r
+ throw new TemplateModelException(t.getMessage());\r
+ }\r
+ }\r
+ }\r
+\r
+ public static class FreemarkerGeneratorLibrary implements GeneratorLibrary {\r
+ private FileTemplateCache templateCache;\r
+\r
+ public FreemarkerGeneratorLibrary(String aTemplateRoot) {\r
+ templateCache = new FileTemplateCache( aTemplateRoot+"/" );\r
+ templateCache.setLoadingPolicy(FileTemplateCache.LOAD_ON_DEMAND);\r
+ }\r
+\r
+ public Generator makeGenerator(String anIdentifier) throws GeneratorExc, GeneratorFailure {\r
+ Template template = (Template) templateCache.getItem(anIdentifier, "template");\r
+\r
+ if (template==null) {\r
+ throw new GeneratorExc("FreemarkerGeneratorLibrary: Can't find template "+templateCache.getDirectory()+anIdentifier);\r
+ }\r
+\r
+ return new FreemarkerGenerator(template);\r
+ }\r
+ }\r
+\r
+ public static class FreemarkerGeneratorLibraryFactory implements GeneratorLibraryFactory {\r
+ private String basePath;\r
+\r
+ public FreemarkerGeneratorLibraryFactory(String aBasePath) {\r
+ basePath = aBasePath;\r
+ }\r
+\r
+ public GeneratorLibrary makeLibrary(String anInitializationString) {\r
+ return new FreemarkerGeneratorLibrary(basePath+anInitializationString);\r
+ };\r
+ }\r
+}\r