gt: 			String name = resource.getFilename();
context: /*
   * Copyright 2013 SpringSource.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  package org.codehaus.groovy.grails.scaffolding;
  
  import grails.build.logging.GrailsConsole;
  import groovy.text.SimpleTemplateEngine;
  import groovy.text.Template;
  
  import java.io.BufferedWriter;
  import java.io.File;
  import java.io.FileWriter;
  import java.io.IOException;
  import java.io.InputStream;
  import java.io.Writer;
  import java.util.Collections;
  import java.util.HashMap;
  import java.util.HashSet;
  import java.util.Map;
  import java.util.Set;
  
  import org.apache.commons.logging.Log;
  import org.apache.commons.logging.LogFactory;
  import org.codehaus.groovy.grails.commons.GrailsApplication;
  import org.codehaus.groovy.grails.commons.GrailsDomainClass;
  import org.codehaus.groovy.grails.commons.GrailsDomainClassProperty;
  import org.codehaus.groovy.grails.plugins.GrailsPluginInfo;
  import org.codehaus.groovy.grails.plugins.GrailsPluginManager;
  import org.codehaus.groovy.grails.plugins.GrailsPluginUtils;
  import org.codehaus.groovy.grails.plugins.PluginManagerAware;
  import org.codehaus.groovy.runtime.IOGroovyMethods;
  import org.codehaus.groovy.runtime.StringGroovyMethods;
  import org.springframework.context.ResourceLoaderAware;
  import org.springframework.core.io.AbstractResource;
  import org.springframework.core.io.FileSystemResource;
  import org.springframework.core.io.Resource;
  import org.springframework.core.io.ResourceLoader;
  import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
  import org.springframework.util.Assert;
  import org.springframework.util.StringUtils;
  
  public abstract class AbstractGrailsTemplateGenerator implements GrailsTemplateGenerator, ResourceLoaderAware, PluginManagerAware {
  
  	protected static final Log log = LogFactory.getLog(AbstractGrailsTemplateGenerator.class);
  
  	protected String basedir = ".";
  	protected boolean overwrite = false;
  	protected SimpleTemplateEngine engine = new SimpleTemplateEngine();
  	protected ResourceLoader resourceLoader;
  	protected Template renderEditorTemplate;
  	protected String domainSuffix = "";
  	protected GrailsPluginManager pluginManager;
  	protected GrailsApplication grailsApplication;
  
  	protected AbstractGrailsTemplateGenerator(ClassLoader classLoader) {
  		engine = new SimpleTemplateEngine(classLoader);
  	}
  
  	public void generateViews(GrailsDomainClass domainClass, String destDir) throws IOException {
  		Assert.hasText(destDir, "Argument [destdir] not specified");
  
  		File viewsDir = new File(destDir, "grails-app/views/" + domainClass.getPropertyName());
  		if (!viewsDir.exists()) {
  			viewsDir.mkdirs();
  		}
  
  		for (String name : getTemplateNames()) {
              if(log.isInfoEnabled())
  			    log.info("Generating ["+name+"] view for domain class ["+domainClass.getFullName()+"]");
  			generateView(domainClass, name, viewsDir.getAbsolutePath());
  		}
  	}
  
  	public void generateController(GrailsDomainClass domainClass, String destDir) throws IOException {
  		Assert.hasText(destDir, "Argument [destdir] not specified");
  
  		if (domainClass == null) {
  			return;
  		}
  
  		String fullName = domainClass.getFullName();
  		String pkg = "";
  		int pos = fullName.lastIndexOf('.');
  		if (pos != -1) {
  			// Package name with trailing '.'
  			pkg = fullName.substring(0, pos + 1);
  		}
  
  		File destFile = new File(destDir, "grails-app/controllers/" + pkg.replace('.', '/') + domainClass.getShortName() + "Controller.groovy");
  		if (canWrite(destFile)) {
  			destFile.getParentFile().mkdirs();
  
  			BufferedWriter writer = null;
  			try {
  				writer = new BufferedWriter(new FileWriter(destFile));
  				generateController(domainClass, writer);
  				try {
  					writer.flush();
  				}
  				catch (IOException ignored) {}
  			}
  			finally {
  				IOGroovyMethods.closeQuietly(writer);
  			}
  
  			log.info("Controller generated at ["+destFile+"]");
  		}
  	}
  
      @Override
      public void generateAsyncController(GrailsDomainClass domainClass, String destDir) throws IOException {
          Assert.hasText(destDir, "Argument [destdir] not specified");
  
          if (domainClass == null) {
              return;
          }
  
          String fullName = domainClass.getFullName();
          String pkg = "";
          int pos = fullName.lastIndexOf('.');
          if (pos != -1) {
              // Package name with trailing '.'
              pkg = fullName.substring(0, pos + 1);
          }
  
          File destFile = new File(destDir, "grails-app/controllers/" + pkg.replace('.', '/') + domainClass.getShortName() + "Controller.groovy");
          if (canWrite(destFile)) {
              destFile.getParentFile().mkdirs();
  
              BufferedWriter writer = null;
              try {
                  writer = new BufferedWriter(new FileWriter(destFile));
                  generateAsyncController(domainClass, writer);
                  try {
                      writer.flush();
                  }
                  catch (IOException ignored) {}
              }
              finally {
                  IOGroovyMethods.closeQuietly(writer);
              }
  
              log.info("Controller generated at ["+destFile+"]");
          }
      }
  
      public void generateView(GrailsDomainClass domainClass, String viewName, Writer out) throws IOException {
  		String templateText = getTemplateText(viewName + ".gsp");
  
  		if (!StringUtils.hasLength(templateText)) {
  			return;
  		}
  
  		GrailsDomainClassProperty multiPart = null;
  		for (GrailsDomainClassProperty property : domainClass.getProperties()) {
  			if (property.getType() == Byte[].class || property.getType() == byte[].class) {
  				multiPart = property;
  				break;
  			}
  		}
  
  		String packageName = StringUtils.hasLength(domainClass.getPackageName()) ? "<%@ page import=\"" + domainClass.getFullName() + "\" %>" : "";
  		Map<String, Object> binding = createBinding(domainClass);
  		binding.put("packageName", packageName);
  		binding.put("multiPart", multiPart);
  		binding.put("propertyName", getPropertyName(domainClass));
  
  		generate(templateText, binding, out);
  	}
  
  	protected abstract Object getRenderEditor();
  
  	public void generateView(GrailsDomainClass domainClass, String viewName, String destDir) throws IOException {
  		File destFile = new File(destDir, viewName + ".gsp");
  		if (!canWrite(destFile)) {
  			return;
  		}
  
  		BufferedWriter writer = null;
  		try {
  			writer = new BufferedWriter(new FileWriter(destFile));
  			generateView(domainClass, viewName, writer);
  			try {
  				writer.flush();
  			}
  			catch (IOException ignored) {}
  		}
  		finally {
  			IOGroovyMethods.closeQuietly(writer);
  		}
  	}
  
  	public void generateController(GrailsDomainClass domainClass, Writer out) throws IOException {
  		String templateText = getTemplateText("Controller.groovy");
  
  		Map<String, Object> binding = createBinding(domainClass);
  		binding.put("packageName", domainClass.getPackageName());
  		binding.put("propertyName", getPropertyName(domainClass));
  
  		generate(templateText, binding, out);
  	}
  
      public void generateAsyncController(GrailsDomainClass domainClass, Writer out) throws IOException {
          String templateText = getTemplateText("AsyncController.groovy");
  
          Map<String, Object> binding = createBinding(domainClass);
          binding.put("packageName", domainClass.getPackageName());
          binding.put("propertyName", getPropertyName(domainClass));
  
          generate(templateText, binding, out);
      }
  
      @Override
      public void generateAsyncTest(GrailsDomainClass domainClass, String destDir) throws IOException {
          generateTest(domainClass, destDir, "AsyncSpec.groovy");
      }
  
  	public void generateTest(GrailsDomainClass domainClass, String destDir) throws IOException {
          generateTest(domainClass, destDir, "Spec.groovy");
  	}
  
      private void generateTest(GrailsDomainClass domainClass, String destDir, String templateName) throws IOException {
          File destFile = new File(destDir, domainClass.getPackageName().replace('.', '/') + '/' + domainClass.getShortName() + "ControllerSpec.groovy");
          if (!canWrite(destFile)) {
              return;
          }
  
          String templateText = getTemplateText(templateName);
  
          Map<String, Object> binding = createBinding(domainClass);
          binding.put("packageName", domainClass.getPackageName());
          binding.put("propertyName", domainClass.getLogicalPropertyName());
          binding.put("modelName", getPropertyName(domainClass));
  
          destFile.getParentFile().mkdirs();
          BufferedWriter writer = null;
          try {
              writer = new BufferedWriter(new FileWriter(destFile));
              generate(templateText, binding, writer);
              try {
                  writer.flush();
              }
              catch (IOException ignored) {}
          }
          finally {
              IOGroovyMethods.closeQuietly(writer);
          }
      }
  
  
      @SuppressWarnings("deprecation")
      protected Map<String, Object> createBinding(GrailsDomainClass domainClass) {
  		boolean hasHibernate = pluginManager.hasGrailsPlugin("hibernate") || pluginManager.hasGrailsPlugin("hibernate4");
  
  		Map<String, Object> binding = new HashMap<String, Object>();
  		binding.put("pluginManager", pluginManager);
  		binding.put("domainClass", domainClass);
  		binding.put("className", domainClass.getShortName());
  		binding.put("renderEditor", getRenderEditor());
  		binding.put("comparator", hasHibernate ? DomainClassPropertyComparator.class : SimpleDomainClassPropertyComparator.class);
  		return binding;
  	}
  
  	protected void generate(String templateText, Map<String, Object> binding, Writer out) {
  		try {
  			engine.createTemplate(templateText).make(binding).writeTo(out);
  		}
  		catch (ClassNotFoundException e) {
  			throw new RuntimeException(e);
  		}
  		catch (IOException e) {
  			throw new RuntimeException(e);
  		}
  	}
  
  	protected String getPropertyName(GrailsDomainClass domainClass) {
  		return domainClass.getPropertyName() + domainSuffix;
  	}
  
  	protected String getTemplateText(String template) throws IOException {
  		InputStream inputStream = null;
  		if (resourceLoader != null && grailsApplication.isWarDeployed()) {
  			inputStream = resourceLoader.getResource("/WEB-INF/templates/scaffolding/" + template).getInputStream();
  		}
  		else {
  			AbstractResource templateFile = getTemplateResource(template);
  			if (templateFile.exists()) {
  				inputStream = templateFile.getInputStream();
  			}
  		}
  
  		return inputStream == null ? null : IOGroovyMethods.getText(inputStream);
  	}
  
  	protected AbstractResource getTemplateResource(String template) throws IOException {
  		String name = "src/templates/scaffolding/" + template;
  		AbstractResource templateFile = new FileSystemResource(new File(basedir, name).getAbsoluteFile());
  		if (!templateFile.exists()) {
  			templateFile = new FileSystemResource(new File(getPluginDir(), name).getAbsoluteFile());
  		}
  
  		return templateFile;
  	}
  
  	protected File getPluginDir() throws IOException {
  		GrailsPluginInfo info = GrailsPluginUtils.getPluginBuildSettings().getPluginInfoForName("scaffolding");
  		return info.getDescriptor().getFile().getParentFile();
  	}
  
  	protected boolean canWrite(File testFile) {
  		if (overwrite || !testFile.exists()) {
  			return true;
  		}
  
  		try {
  			String relative = makeRelativeIfPossible(testFile.getAbsolutePath(), basedir);
  			String response = GrailsConsole.getInstance().userInput(
  					"File " + relative + " already exists. Overwrite?", new String[] { "y", "n", "a" });
  			overwrite = overwrite || "a".equals(response);
  			return overwrite || "y".equals(response);
  		}
  		catch (Exception e) {
  			// failure to read from standard in means we're probably running from an automation tool like a build server
  			return true;
  		}
  	}
  
  	protected String makeRelativeIfPossible(String fileName, String base) throws IOException {
  		if (StringUtils.hasLength(base)) {
  			fileName = StringGroovyMethods.minus(fileName, new File(base).getCanonicalPath());
  		}
  		return fileName;
  	}
  
  	protected Set<String> getTemplateNames() throws IOException {
  
  		if (resourceLoader != null && grailsApplication.isWarDeployed()) {
  			try {
  				PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(resourceLoader);
  				return extractNames(resolver.getResources("/WEB-INF/templates/scaffolding/*.gsp"));
  			}
  			catch (Exception e) {
  				return Collections.emptySet();
  			}
  		}
  
  		PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
  		Set<String> resources = new HashSet<String>();
  
  		String templatesDirPath = basedir + "/src/templates/scaffolding";
  		Resource templatesDir = new FileSystemResource(templatesDirPath);
  		if (templatesDir.exists()) {
  			try {
  				resources.addAll(extractNames(resolver.getResources("file:" + templatesDirPath + "/*.gsp")));
  			}
  			catch (Exception e) {
  				log.error("Error while loading views from " + basedir, e);
  			}
  		}
  
  		File pluginDir = getPluginDir();
  		try {
  			resources.addAll(extractNames(resolver.getResources("file:" + pluginDir + "/src/templates/scaffolding/*.gsp")));
  		}
  		catch (Exception e) {
  			// ignore
  			log.error("Error locating templates from " + pluginDir + ": " + e.getMessage(), e);
  		}
  
  		return resources;
  	}
  
  	protected Set<String> extractNames(Resource[] resources) {
  		Set<String> names = new HashSet<String>();
  		for (Resource resource : resources) {
  