AbstractGriffonView.java
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 }