001 /*
002 * Copyright 2010-2012 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017 package org.codehaus.griffon.runtime.core;
018
019 import griffon.core.GriffonView;
020 import griffon.core.GriffonViewClass;
021 import griffon.util.Xml2Groovy;
022 import groovy.lang.GroovyShell;
023 import groovy.lang.Script;
024 import groovy.util.FactoryBuilderSupport;
025
026 import java.io.InputStream;
027 import java.util.Map;
028
029 import static griffon.util.GriffonNameUtils.isBlank;
030
031 /**
032 * Base implementation of the GriffonView interface.
033 *
034 * @author Andres Almiray
035 * @since 0.9.1
036 */
037 public abstract class AbstractGriffonView extends AbstractGriffonMvcArtifact implements GriffonView {
038 private FactoryBuilderSupport builder;
039
040 protected String getArtifactType() {
041 return GriffonViewClass.TYPE;
042 }
043
044 public FactoryBuilderSupport getBuilder() {
045 return builder;
046 }
047
048 public void setBuilder(FactoryBuilderSupport builder) {
049 this.builder = builder;
050 }
051
052 /**
053 * Transforms an XML file into a Groovy script and evaluates it using a builder.</p>
054 * <p>The file name matches the name of this class plus '.xml'. It must be found somewhere
055 * in the classpath.</p>
056 * <p>Every XML attribute that represents a string literal must be single quoted explicitly
057 * otherwise the build will not be able to parse it. The following XML contents</p>
058 * <pre><xmp>
059 * <application title="app.config.application.title"
060 * pack="true">
061 * <actions>
062 * <action id="'clickAction'"
063 * name="'Click'"
064 * closure="{controller.click(it)}"/>
065 * </actions>
066 * <gridLayout cols="1" rows="3"/>
067 * <textField id="'input'" columns="20"
068 * text="bind('value', target: model)"/>
069 * <textField id="'output'" columns="20"
070 * text="bind{model.value}" editable="false"/>
071 * <button action="clickAction"/>
072 * </application>
073 * </xmp></pre>
074 * <p/>
075 * <p>are translated to</p>
076 * <pre>
077 * application(title: app.config.application.title, pack: true) {
078 * actions {
079 * action(id: 'clickAction', name: 'Click', closure: {controller.click(it)})
080 * }
081 * gridLayout(cols: 1, rows: 3)
082 * textField(id: 'input', text: bind('value', target: model), columns: 20)
083 * textField(id: 'output', text: bind{target.model}, columns: 20, editable: false)
084 * button(action: clickAction)
085 * }
086 * </pre>
087 *
088 * @param args a Map containing all relevant values that the build might need to build the
089 * View; this typically includes 'app', 'controller' and 'model'.
090 * @since 0.9.2
091 */
092 public void buildViewFromXml(Map<String, Object> args) {
093 buildViewFromXml(args, getClass().getName().replace('.', '/') + ".xml");
094 }
095
096 /**
097 * Transforms an XML file into a Groovy script and evaluates it using a builder.</p>
098 * <p>Every XML attribute that represents a string literal must be single quoted explicitly
099 * otherwise the build will not be able to parse it. The following XML contents</p>
100 * <pre><xmp>
101 * <application title="app.config.application.title"
102 * pack="true">
103 * <actions>
104 * <action id="'clickAction'"
105 * name="'Click'"
106 * closure="{controller.click(it)}"/>
107 * </actions>
108 * <gridLayout cols="1" rows="3"/>
109 * <textField id="'input'" columns="20"
110 * text="bind('value', target: model)"/>
111 * <textField id="'output'" columns="20"
112 * text="bind{model.value}" editable="false"/>
113 * <button action="clickAction"/>
114 * </application>
115 * </xmp></pre>
116 * <p/>
117 * <p>are translated to</p>
118 * <pre>
119 * application(title: app.config.application.title, pack: true) {
120 * actions {
121 * action(id: 'clickAction', name: 'Click', closure: {controller.click(it)})
122 * }
123 * gridLayout(cols: 1, rows: 3)
124 * textField(id: 'input', text: bind('value', target: model), columns: 20)
125 * textField(id: 'output', text: bind{target.model}, columns: 20, editable: false)
126 * button(action: clickAction)
127 * }
128 * </pre>
129 *
130 * @param args a Map containing all relevant values that the build might need to build the
131 * View; this typically includes 'app', 'controller' and 'model'.
132 * @param fileName the name of an XML file
133 * @since 0.9.2
134 */
135 public void buildViewFromXml(Map<String, Object> args, String fileName) {
136 if (isBlank(fileName)) {
137 throw new IllegalArgumentException("Invalid file name for externalized view.");
138 }
139
140 InputStream is = getClass().getClassLoader().getResourceAsStream(fileName);
141 if (is == null) {
142 throw new IllegalArgumentException("Could not read file " + fileName);
143 }
144
145 String groovyScript = Xml2Groovy.getInstance().parse(is);
146 if (isBlank(groovyScript)) {
147 throw new IllegalArgumentException("File " + fileName + " is empty.");
148 } else if (getLog().isTraceEnabled()) {
149 getLog().trace("View script for " + fileName + "\n" + groovyScript);
150 }
151
152 final Script script = new GroovyShell().parse(groovyScript);
153 script.setBinding(getBuilder());
154 for (Map.Entry<String, ?> arg : args.entrySet()) {
155 script.getBinding().setVariable(arg.getKey(), arg.getValue());
156 }
157
158 getApp().execSync(new Runnable() {
159 public void run() {
160 getBuilder().build(script);
161 }
162 });
163 }
164 }
|