public BaseSettingsApi(
      BuildSettings settings, GrailsBuildEventListener buildEventListener, boolean interactive) {
    buildSettings = settings;
    buildProps = buildSettings.getConfig().toProperties();
    grailsHome = buildSettings.getGrailsHome();

    metadataFile = new File(buildSettings.getBaseDir(), "application.properties");

    metadata = metadataFile.exists() ? Metadata.getInstance(metadataFile) : Metadata.getCurrent();

    metadataFile = metadata.getMetadataFile();
    enableProfile = Boolean.valueOf(getPropertyValue("grails.script.profile", false).toString());
    pluginsHome = buildSettings.getProjectPluginsDir().getPath();
    pluginSettings = GrailsPluginUtils.getPluginBuildSettings(settings);
    grailsAppName = metadata.getApplicationName();
    isInteractive = interactive;

    // If no app name property (upgraded/new/edited project) default to basedir.
    if (grailsAppName == null) {
      grailsAppName = buildSettings.getBaseDir().getName();
    }

    if (grailsAppName.indexOf('/') > -1) {
      appClassName =
          grailsAppName.substring(grailsAppName.lastIndexOf('/'), grailsAppName.length());
    } else {
      appClassName = GrailsNameUtils.getClassNameRepresentation(grailsAppName);
    }
    configSlurper = buildSettings.createConfigSlurper();
    configSlurper.setEnvironment(buildSettings.getGrailsEnv());
    this.buildEventListener = buildEventListener;
  }
  public void updateMetadata(
      @SuppressWarnings("hiding") Metadata metadata, @SuppressWarnings("rawtypes") Map entries) {
    for (Object key : entries.keySet()) {
      final Object value = entries.get(key);
      if (value != null) {
        metadata.put(key, value.toString());
      }
    }

    metadata.persist();
  }
  @Override
  public void setResourceLoader(ResourceLoader resourceLoader) {
    if (Metadata.getCurrent().isWarDeployed()) {
      localResourceLoader = resourceLoader;
    } else {
      // The "settings" may be null in some of the Grails unit tests.
      BuildSettings settings = BuildSettingsHolder.getSettings();

      String location = null;
      if (settings != null) {
        location = settings.getResourcesDir().getPath();
      } else if (Environment.getCurrent().isReloadEnabled()) {
        location = Environment.getCurrent().getReloadLocation();
      }

      if (location != null) {
        localResourceLoader = new DevelopmentResourceLoader(application, location);
      } else {
        localResourceLoader = resourceLoader;
      }
    }
    super.setResourceLoader(localResourceLoader);

    if (resourceResolver == null) {
      resourceResolver = new PathMatchingResourcePatternResolver(localResourceLoader);
    }
  }
 private Resource getResourceWithinContext(String uri) {
   if (resourceLoader == null)
     throw new IllegalStateException(
         "TemplateEngine not initialised correctly, no [resourceLoader] specified!");
   if (Environment.getCurrent().isReloadEnabled() && Metadata.getCurrent().isWarDeployed()) {
     return resourceLoader.getResource(uri);
   }
   Resource r = servletContextLoader.getResource(uri);
   if (r.exists()) return r;
   return resourceLoader.getResource(uri);
 }
  protected void syncAppVersion() {
    final Metadata metadata =
        Metadata.getInstance(new File(getBasedir(), "application.properties"));
    if (syncVersion(metadata)) metadata.persist();

    String artifactId = project.getArtifactId();

    if (artifactId.startsWith("grails-")) artifactId = artifactId.substring("grails-".length());

    final String fName = getFullGrailsPluginName();
    if (fName != null) {
      File gpFile = new File(fName);

      String text = null;
      String mod = null;
      try {
        text = readFileAsString(gpFile);
        mod = text.replaceFirst(GRAILS_PLUGIN_VERSION_PATTERN, "$1" + project.getVersion() + "$5");
      } catch (IOException e) {
        // ignore
      }
      if (text != null && !mod.equalsIgnoreCase(text)) {
        BufferedOutputStream out = null;
        try {
          out = new BufferedOutputStream(new FileOutputStream(gpFile));
          out.write(mod.getBytes());
        } catch (IOException e) {
          // do nuffink
        } finally {
          if (out != null) {
            try {
              out.close();
            } catch (Exception ignored) {
              // ignored
            }
          }
        }
      }
    }
  }
  private boolean syncVersion(Metadata metadata) {
    boolean changed = false;

    Object apGrailsVersion = metadata.get(APP_GRAILS_VERSION);

    Artifact grailsDependency = findGrailsDependency(project);
    if (grailsDependency != null) {
      if (!grailsDependency.getVersion().equals(apGrailsVersion)) {
        metadata.put(APP_GRAILS_VERSION, grailsDependency.getVersion());
        changed = true;
      }
    } else if (grailsVersion != null && !grailsVersion.equals(apGrailsVersion)) {
      metadata.put(APP_GRAILS_VERSION, grailsVersion);
      changed = true;
    }

    if (!project.getVersion().equals(metadata.get(APP_VERSION))) {
      metadata.put(APP_VERSION, project.getVersion());
      changed = true;
    }

    return changed;
  }
 private boolean enableDocumentationGeneration() {
   return !Metadata.getCurrent().isWarDeployed() && isBasePlugin();
 }
  @SuppressWarnings("unchecked")
  private void evaluateOnChangeListener() {
    if (pluginBean.isReadableProperty(ON_SHUTDOWN)) {
      onShutdownListener =
          (Closure) GrailsClassUtils.getPropertyOrStaticPropertyOrFieldValue(plugin, ON_SHUTDOWN);
    }
    if (pluginBean.isReadableProperty(ON_CONFIG_CHANGE)) {
      onConfigChangeListener =
          (Closure)
              GrailsClassUtils.getPropertyOrStaticPropertyOrFieldValue(plugin, ON_CONFIG_CHANGE);
    }
    if (pluginBean.isReadableProperty(ON_CHANGE)) {
      onChangeListener =
          (Closure) GrailsClassUtils.getPropertyOrStaticPropertyOrFieldValue(plugin, ON_CHANGE);
    }

    final boolean warDeployed = Metadata.getCurrent().isWarDeployed();
    final boolean reloadEnabled = Environment.getCurrent().isReloadEnabled();

    if (!((reloadEnabled || !warDeployed) && onChangeListener != null)) {
      return;
    }

    Object referencedResources =
        GrailsClassUtils.getPropertyOrStaticPropertyOrFieldValue(plugin, WATCHED_RESOURCES);

    try {
      List resourceList = null;
      if (referencedResources instanceof String) {
        if (LOG.isDebugEnabled()) {
          LOG.debug(
              "Configuring plugin "
                  + this
                  + " to watch resources with pattern: "
                  + referencedResources);
        }
        resourceList = new ArrayList();
        resourceList.add(referencedResources.toString());
      } else if (referencedResources instanceof List) {
        resourceList = (List) referencedResources;
      }

      if (resourceList != null) {
        List<String> resourceListTmp = new ArrayList<String>();
        PluginBuildSettings pluginBuildSettings = GrailsPluginUtils.getPluginBuildSettings();

        if (pluginBuildSettings != null) {

          final Resource[] pluginDirs = pluginBuildSettings.getPluginDirectories();
          final Environment env = Environment.getCurrent();
          final String baseLocation = env.getReloadLocation();

          for (Object ref : resourceList) {
            String stringRef = ref.toString();
            if (!warDeployed) {
              for (Resource pluginDir : pluginDirs) {
                if (pluginDir != null) {
                  String pluginResources =
                      getResourcePatternForBaseLocation(
                          pluginDir.getFile().getCanonicalPath(), stringRef);
                  resourceListTmp.add(pluginResources);
                }
              }
              addBaseLocationPattern(resourceListTmp, baseLocation, stringRef);
            } else {
              addBaseLocationPattern(resourceListTmp, baseLocation, stringRef);
            }
          }

          watchedResourcePatternReferences = new String[resourceListTmp.size()];
          for (int i = 0; i < watchedResourcePatternReferences.length; i++) {
            String resRef = resourceListTmp.get(i);
            watchedResourcePatternReferences[i] = resRef;
          }

          watchedResourcePatterns =
              new WatchPatternParser()
                  .getWatchPatterns(Arrays.asList(watchedResourcePatternReferences));
        }
      }
    } catch (IllegalArgumentException e) {
      if (GrailsUtil.isDevelopmentEnv()) {
        LOG.debug(
            "Cannot load plug-in resource watch list from ["
                + ArrayUtils.toString(watchedResourcePatternReferences)
                + "]. This means that the plugin "
                + this
                + ", will not be able to auto-reload changes effectively. Try runnng grails upgrade.: "
                + e.getMessage());
      }
    } catch (IOException e) {
      if (GrailsUtil.isDevelopmentEnv()) {
        LOG.debug(
            "Cannot load plug-in resource watch list from ["
                + ArrayUtils.toString(watchedResourcePatternReferences)
                + "]. This means that the plugin "
                + this
                + ", will not be able to auto-reload changes effectively. Try runnng grails upgrade.: "
                + e.getMessage());
      }
    }
  }
/**
 * Default implementation of the GrailsApplication interface that manages application loading,
 * state, and artefact instances.
 *
 * <p>Upon loading this GrailsApplication will inspect each class using its registered
 * ArtefactHandler instances. Each ArtefactHandler provides knowledge about the conventions used to
 * establish its artefact type. For example controllers use the ControllerArtefactHandler to
 * establish this knowledge.
 *
 * <p>New ArtefactHandler instances can be registered with the GrailsApplication thus allowing
 * application extensibility.
 *
 * @author Marc Palmer
 * @author Steven Devijver
 * @author Graeme Rocher
 * @see org.codehaus.groovy.grails.plugins.GrailsPluginManager
 * @see org.codehaus.groovy.grails.plugins.DefaultGrailsPluginManager
 * @see org.codehaus.groovy.grails.commons.ArtefactHandler
 * @see org.codehaus.groovy.grails.commons.ArtefactInfo
 * @since 0.1
 */
public class DefaultGrailsApplication extends GroovyObjectSupport
    implements GrailsApplication, BeanClassLoaderAware {

  protected static final Pattern GETCLASSESPROP_PATTERN = Pattern.compile("(\\w+)(Classes)");
  protected static final Pattern GETCLASSESMETH_PATTERN = Pattern.compile("(get)(\\w+)(Classes)");
  protected static final Pattern ISCLASS_PATTERN = Pattern.compile("(is)(\\w+)(Class)");
  protected static final Pattern GETCLASS_PATTERN = Pattern.compile("(get)(\\w+)Class");

  protected ClassLoader cl;

  protected Class<?>[] allClasses = new Class[0];
  protected static Log log = LogFactory.getLog(DefaultGrailsApplication.class);
  protected ApplicationContext parentContext;
  protected ApplicationContext mainContext;

  protected List<Class<?>> loadedClasses = new ArrayList<Class<?>>();
  protected ArtefactHandler[] artefactHandlers;
  protected Map<String, ArtefactHandler> artefactHandlersByName =
      new HashMap<String, ArtefactHandler>();
  protected List<Class<?>> allArtefactClasses = new ArrayList<Class<?>>();
  protected Map<String, ArtefactInfo> artefactInfo = new HashMap<String, ArtefactInfo>();
  protected Class<?>[] allArtefactClassesArray;
  protected Metadata applicationMeta = Metadata.getCurrent();
  protected Resource[] resources;
  protected boolean initialised = false;
  protected ConfigObject config;

  @SuppressWarnings("rawtypes")
  protected Map flatConfig = Collections.emptyMap();

  /** Creates a new empty Grails application. */
  public DefaultGrailsApplication() {
    cl = new GroovyClassLoader();
  }

  /**
   * Creates a new GrailsApplication instance using the given classes and GroovyClassLoader.
   *
   * @param classes The classes that make up the GrailsApplication
   * @param classLoader The GroovyClassLoader to use
   */
  public DefaultGrailsApplication(final Class<?>[] classes, ClassLoader classLoader) {
    Assert.notNull(classes, "Constructor argument 'classes' cannot be null");

    loadedClasses.addAll(Arrays.asList(classes));
    allClasses = classes;
    cl = classLoader;
  }

  /**
   * Loads a GrailsApplication using the given ResourceLocator instance which will search for
   * appropriate class names
   */
  public DefaultGrailsApplication(Resource[] resources) {
    this();
    for (Resource resource : resources) {

      Class<?> aClass;
      try {
        aClass =
            cl.loadClass(
                org.codehaus.groovy.grails.io.support.GrailsResourceUtils.getClassName(
                    resource.getFile().getAbsolutePath()));
      } catch (ClassNotFoundException e) {
        throw new GrailsConfigurationException(
            "Class not found loading Grails application: " + e.getMessage(), e);
      } catch (IOException e) {
        throw new GrailsConfigurationException(
            "Class not found loading Grails application: " + e.getMessage(), e);
      }
      loadedClasses.add(aClass);
    }
  }

  /**
   * Loads a GrailsApplication using the given ResourceLocator instance which will search for
   * appropriate class names
   */
  public DefaultGrailsApplication(org.codehaus.groovy.grails.io.support.Resource[] resources) {
    this();
    for (org.codehaus.groovy.grails.io.support.Resource resource : resources) {

      Class<?> aClass;
      try {
        aClass =
            cl.loadClass(
                org.codehaus.groovy.grails.io.support.GrailsResourceUtils.getClassName(
                    resource.getFile().getAbsolutePath()));
      } catch (ClassNotFoundException e) {
        throw new GrailsConfigurationException(
            "Class not found loading Grails application: " + e.getMessage(), e);
      } catch (IOException e) {
        throw new GrailsConfigurationException(
            "Class not found loading Grails application: " + e.getMessage(), e);
      }
      loadedClasses.add(aClass);
    }
  }
  /**
   * Initialises the default set of ArtefactHandler instances.
   *
   * @see org.codehaus.groovy.grails.commons.ArtefactHandler
   */
  protected void initArtefactHandlers() {

    final DomainClassArtefactHandler domainClassArtefactHandler =
        new AnnotationDomainClassArtefactHandler();
    if (!hasArtefactHandler(domainClassArtefactHandler.getType())) {
      registerArtefactHandler(domainClassArtefactHandler);
    }

    final ControllerArtefactHandler controllerArtefactHandler = new ControllerArtefactHandler();
    if (!hasArtefactHandler(controllerArtefactHandler.getType())) {
      registerArtefactHandler(controllerArtefactHandler);
    }

    final ServiceArtefactHandler serviceArtefactHandler = new ServiceArtefactHandler();
    if (!hasArtefactHandler(serviceArtefactHandler.getType())) {
      registerArtefactHandler(serviceArtefactHandler);
    }

    final TagLibArtefactHandler tagLibArtefactHandler = new TagLibArtefactHandler();
    if (!hasArtefactHandler(tagLibArtefactHandler.getType())) {
      registerArtefactHandler(tagLibArtefactHandler);
    }

    final BootstrapArtefactHandler bootstrapArtefactHandler = new BootstrapArtefactHandler();
    if (!hasArtefactHandler(bootstrapArtefactHandler.getType())) {
      registerArtefactHandler(bootstrapArtefactHandler);
    }

    final CodecArtefactHandler codecArtefactHandler = new CodecArtefactHandler();
    if (!hasArtefactHandler(codecArtefactHandler.getType())) {
      registerArtefactHandler(codecArtefactHandler);
    }

    final UrlMappingsArtefactHandler urlMappingsArtefactHandler = new UrlMappingsArtefactHandler();
    if (!hasArtefactHandler(urlMappingsArtefactHandler.getType())) {
      registerArtefactHandler(urlMappingsArtefactHandler);
    }

    updateArtefactHandlers();
  }

  private void updateArtefactHandlers() {
    // Cache the list as an array
    artefactHandlers =
        artefactHandlersByName.values().toArray(new ArtefactHandler[artefactHandlersByName.size()]);
  }

  /**
   * Returns all the classes identified as artefacts by ArtefactHandler instances.
   *
   * @return An array of classes
   */
  public Class<?>[] getAllArtefacts() {
    return allArtefactClassesArray;
  }

  protected Class<?>[] populateAllClasses() {
    allClasses = loadedClasses.toArray(new Class[loadedClasses.size()]);
    return allClasses;
  }

  /**
   * Configures the loaded classes within the GrailsApplication instance using the registered
   * ArtefactHandler instances.
   *
   * @param classes The classes to configure
   */
  protected void configureLoadedClasses(Class<?>[] classes) {

    initArtefactHandlers();

    artefactInfo.clear();
    allArtefactClasses.clear();
    allArtefactClassesArray = null;
    allClasses = classes;

    // first load the domain classes
    log.debug("Going to inspect artefact classes.");
    for (final Class<?> theClass : classes) {
      log.debug("Inspecting [" + theClass.getName() + "]");
      if (allArtefactClasses.contains(theClass)) {
        continue;
      }

      // check what kind of artefact it is and add to corrent data structure
      for (ArtefactHandler artefactHandler : artefactHandlers) {
        if (artefactHandler.isArtefact(theClass)) {
          log.debug("Adding artefact " + theClass + " of kind " + artefactHandler.getType());
          GrailsClass gclass = addArtefact(artefactHandler.getType(), theClass);
          // Also maintain set of all artefacts (!= all classes loaded)
          allArtefactClasses.add(theClass);

          // Update per-artefact cache
          DefaultArtefactInfo info = getArtefactInfo(artefactHandler.getType(), true);
          info.addGrailsClass(gclass);
          break;
        }
      }
    }

    refreshArtefactGrailsClassCaches();

    allArtefactClassesArray = allArtefactClasses.toArray(new Class[allArtefactClasses.size()]);

    // Tell all artefact handlers to init now we've worked out which classes are which artefacts
    for (ArtefactHandler artefactHandler : artefactHandlers) {
      initializeArtefacts(artefactHandler);
    }
  }

  /**
   * Tell all our artefact info objects to update their internal state after we've added a bunch of
   * classes.
   */
  protected void refreshArtefactGrailsClassCaches() {
    for (Object o : artefactInfo.values()) {
      ((DefaultArtefactInfo) o).updateComplete();
    }
  }

  protected void addToLoaded(Class<?> clazz) {
    loadedClasses.add(clazz);
    populateAllClasses();
  }

  public ClassLoader getClassLoader() {
    return cl;
  }

  public ConfigObject getConfig() {
    if (config == null) {
      setConfig(ConfigurationHelper.loadConfigFromClasspath(this));
    }
    return config;
  }

  public void setConfig(ConfigObject config) {
    this.config = config;
    if (config == null) {
      flatConfig = Collections.emptyMap();
    } else {
      flatConfig = config.flatten();
    }
  }

  @SuppressWarnings("unchecked")
  public Map<String, Object> getFlatConfig() {
    return flatConfig;
  }

  /**
   * Retrieves the number of artefacts registered for the given artefactType as defined by the
   * ArtefactHandler.
   *
   * @param artefactType The type of the artefact as defined by the ArtefactHandler
   * @return The number of registered artefacts
   */
  protected int getArtefactCount(String artefactType) {
    ArtefactInfo info = getArtefactInfo(artefactType);
    return info == null ? 0 : info.getClasses().length;
  }

  /**
   * Retrieves all classes loaded by the GrailsApplication.
   *
   * @return All classes loaded by the GrailsApplication
   */
  public Class<?>[] getAllClasses() {
    return allClasses;
  }

  public ApplicationContext getMainContext() {
    return mainContext;
  }

  public void setMainContext(ApplicationContext context) {
    mainContext = context;
  }

  /**
   * Sets the parent ApplicationContext for the GrailsApplication.
   *
   * @param applicationContext The ApplicationContext
   * @throws BeansException Thrown when an error occurs setting the ApplicationContext
   */
  public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    parentContext = applicationContext;
  }

  /**
   * Retrieves the parent ApplicationContext for this GrailsApplication.
   *
   * @return The parent ApplicationContext
   */
  public ApplicationContext getParentContext() {
    return parentContext;
  }

  /**
   * Retrieves a class from the GrailsApplication for the given name.
   *
   * @param className The class name
   * @return Either the Class instance or null if it doesn't exist
   */
  public Class<?> getClassForName(String className) {
    if (StringUtils.isBlank(className)) {
      return null;
    }

    for (Class<?> c : allClasses) {
      if (c.getName().equals(className)) {
        return c;
      }
    }
    return null;
  }

  /**
   * Refreshes constraints defined by the DomainClassArtefactHandler.
   *
   * <p>TODO: Move this out of GrailsApplication
   */
  public void refreshConstraints() {
    ArtefactInfo info = getArtefactInfo(DomainClassArtefactHandler.TYPE, true);
    GrailsClass[] domainClasses = info.getGrailsClasses();
    for (GrailsClass domainClass : domainClasses) {
      ((GrailsDomainClass) domainClass).refreshConstraints();
    }
  }

  /**
   * Refreshes this GrailsApplication, rebuilding all of the artefact definitions as defined by the
   * registered ArtefactHandler instances.
   */
  public void refresh() {
    if (cl instanceof GroovyClassLoader) {
      configureLoadedClasses(((GroovyClassLoader) cl).getLoadedClasses());
    }
  }

  public void rebuild() {
    initialised = false;
    loadedClasses.clear();
    initArtefactHandlers();

    if (GrailsUtil.isDevelopmentEnv()) {
      initialise();
    } else {
      throw new IllegalStateException(
          "Cannot rebuild GrailsApplication when not in development mode!");
    }
  }

  /**
   * Retrieves the Spring Resource that was used to load the given Class.
   *
   * @param theClazz The class
   * @return Either a Spring Resource or null if no Resource was found for the given class
   */
  public Resource getResourceForClass(@SuppressWarnings("rawtypes") Class theClazz) {

    // TODO fix
    return null;
  }

  /**
   * Returns true if the given class is an artefact identified by one of the registered
   * ArtefactHandler instances. Uses class name equality to handle class reloading
   *
   * @param theClazz The class to check
   * @return true if it is an artefact
   */
  public boolean isArtefact(@SuppressWarnings("rawtypes") Class theClazz) {
    String className = theClazz.getName();
    for (Class<?> artefactClass : allArtefactClasses) {
      if (className.equals(artefactClass.getName())) {
        return true;
      }
    }
    return false;
  }

  /**
   * Returns true if the specified class is of the given artefact type as defined by the
   * ArtefactHandler.
   *
   * @param artefactType The type of the artefact
   * @param theClazz The class
   * @return true if it is of the specified artefactType
   * @see org.codehaus.groovy.grails.commons.ArtefactHandler
   */
  public boolean isArtefactOfType(
      String artefactType, @SuppressWarnings("rawtypes") Class theClazz) {
    ArtefactHandler handler = artefactHandlersByName.get(artefactType);
    if (handler == null) {
      throw new GrailsConfigurationException(
          "Unable to locate arefact handler for specified type: " + artefactType);
    }

    return handler.isArtefact(theClazz);
  }

  /**
   * Returns true if the specified class name is of the given artefact type as defined by the
   * ArtefactHandler.
   *
   * @param artefactType The type of the artefact
   * @param className The class name
   * @return true if it is of the specified artefactType
   * @see org.codehaus.groovy.grails.commons.ArtefactHandler
   */
  public boolean isArtefactOfType(String artefactType, String className) {
    return getArtefact(artefactType, className) != null;
  }

  /**
   * Retrieves an artefact for the given type and name.
   *
   * @param artefactType The artefact type as defined by a registered ArtefactHandler
   * @param name The name of the class
   * @return A GrailsClass instance or null if none could be found for the given artefactType and
   *     name
   */
  public GrailsClass getArtefact(String artefactType, String name) {
    ArtefactInfo info = getArtefactInfo(artefactType);
    return info == null ? null : info.getGrailsClass(name);
  }

  public ArtefactHandler getArtefactType(@SuppressWarnings("rawtypes") Class theClass) {
    for (ArtefactHandler artefactHandler : artefactHandlers) {
      if (artefactHandler.isArtefact(theClass)) {
        return artefactHandler;
      }
    }
    return null;
  }

  protected GrailsClass getFirstArtefact(String artefactType) {
    ArtefactInfo info = getArtefactInfo(artefactType);
    // This will throw AIOB if we have none
    return info == null ? null : info.getGrailsClasses()[0];
  }

  /**
   * Returns all of the GrailsClass instances for the given artefactType as defined by the
   * ArtefactHandler
   *
   * @param artefactType The type of the artefact defined by the ArtefactHandler
   * @return An array of classes for the given artefact
   */
  public GrailsClass[] getArtefacts(String artefactType) {
    return getArtefactInfo(artefactType, true).getGrailsClasses();
  }

  // This is next call is equiv to getControllerByURI / getTagLibForTagName
  public GrailsClass getArtefactForFeature(String artefactType, Object featureID) {
    return artefactHandlersByName.get(artefactType).getArtefactForFeature(featureID);
  }

  /**
   * Adds an artefact of the given type for the given Class.
   *
   * @param artefactType The type of the artefact as defined by a ArtefactHandler instance
   * @param artefactClass A Class instance that matches the type defined by the ArtefactHandler
   * @return The GrailsClass if successful or null if it couldn't be added
   * @throws GrailsConfigurationException If the specified Class is not the same as the type defined
   *     by the ArtefactHandler
   * @see org.codehaus.groovy.grails.commons.ArtefactHandler
   */
  public GrailsClass addArtefact(
      String artefactType, @SuppressWarnings("rawtypes") Class artefactClass) {
    return addArtefact(artefactType, artefactClass, false);
  }

  /**
   * Adds an artefact of the given type for the given GrailsClass.
   *
   * @param artefactType The type of the artefact as defined by a ArtefactHandler instance
   * @param artefactGrailsClass A GrailsClass instance that matches the type defined by the
   *     ArtefactHandler
   * @return The GrailsClass if successful or null if it couldn't be added
   * @throws GrailsConfigurationException If the specified GrailsClass is not the same as the type
   *     defined by the ArtefactHandler
   * @see org.codehaus.groovy.grails.commons.ArtefactHandler
   */
  public GrailsClass addArtefact(String artefactType, GrailsClass artefactGrailsClass) {
    ArtefactHandler handler = artefactHandlersByName.get(artefactType);
    if (handler.isArtefactGrailsClass(artefactGrailsClass)) {
      // Store the GrailsClass in cache
      DefaultArtefactInfo info = getArtefactInfo(artefactType, true);
      info.addGrailsClass(artefactGrailsClass);
      info.updateComplete();

      initializeArtefacts(artefactType);

      return artefactGrailsClass;
    }

    throw new GrailsConfigurationException(
        "Cannot add "
            + artefactType
            + " class ["
            + artefactGrailsClass
            + "]. It is not a "
            + artefactType
            + "!");
  }

  /**
   * Registers a new ArtefactHandler that is responsible for identifying and managing a particular
   * artefact type that is defined by some convention.
   *
   * @param handler The ArtefactHandler to regster
   */
  public void registerArtefactHandler(ArtefactHandler handler) {
    GrailsApplicationAwareBeanPostProcessor.processAwareInterfaces(this, handler);
    artefactHandlersByName.put(handler.getType(), handler);
    updateArtefactHandlers();
  }

  public boolean hasArtefactHandler(String type) {
    return artefactHandlersByName.containsKey(type);
  }

  public ArtefactHandler[] getArtefactHandlers() {
    return artefactHandlers;
  }

  public ArtefactHandler getArtefactHandler(String type) {
    return artefactHandlersByName.get(type);
  }

  /**
   * Re-initialize the artefacts of the specified type. This gives handlers a chance to update
   * caches etc.
   *
   * @param artefactType The type of artefact to init
   */
  protected void initializeArtefacts(String artefactType) {
    initializeArtefacts(artefactHandlersByName.get(artefactType));
  }

  /**
   * Clears the application returning it to an empty state. Very dangerous method, use with caution.
   */
  public void clear() {
    artefactHandlersByName.clear();
    updateArtefactHandlers();
    artefactInfo.clear();
    initialise();
  }

  /**
   * Re-initialize the artefacts of the specified type. This gives handlers a chance to update
   * caches etc.
   *
   * @param handler The handler to register
   */
  protected void initializeArtefacts(ArtefactHandler handler) {
    if (handler == null) {
      return;
    }

    ArtefactInfo info = getArtefactInfo(handler.getType());
    // Only init those that have data
    if (info != null) {
      // System.out.println("Initialising artefacts of kind " + handler.getType() + " with
      // registered artefacts" + info.getGrailsClassesByName());
      handler.initialize(info);
    }
  }

  /**
   * Get or create the cache of classes for the specified artefact type.
   *
   * @param artefactType The name of an artefact type
   * @param create Set to true if you want non-existent caches to be created
   * @return The cache of classes for the type, or null if no cache exists and create is false
   */
  protected DefaultArtefactInfo getArtefactInfo(String artefactType, boolean create) {
    DefaultArtefactInfo cache = (DefaultArtefactInfo) artefactInfo.get(artefactType);
    if (cache == null && create) {
      cache = new DefaultArtefactInfo();
      artefactInfo.put(artefactType, cache);
      cache.updateComplete();
    }
    return cache;
  }

  /**
   * Get the cache of classes for the specified artefact type.
   *
   * @param artefactType The name of an artefact type
   * @return The cache of classes for the type, or null if no cache exists
   */
  public ArtefactInfo getArtefactInfo(String artefactType) {
    return getArtefactInfo(artefactType, false);
  }

  /**
   * Overrides method invocation to return dynamic artefact methods.
   *
   * <p>We will support getXXXXClasses() and isXXXXClass(class)
   *
   * @param methodName The name of the method
   * @param args The arguments to the method
   * @return The return value of the method TODO Need to add matches for
   *     add<Artefact>Class(java.lang.Class) and add<Artefact>Class(GrailsClass)
   */
  @Override
  public Object invokeMethod(String methodName, Object args) {

    Object[] argsv = (Object[]) args;

    Matcher match = GETCLASS_PATTERN.matcher(methodName);
    // look for getXXXXClass(y)
    match.find();
    if (match.matches()) {
      if (argsv.length > 0) {
        if (argsv[0] instanceof CharSequence) argsv[0] = argsv[0].toString();
        if ((argsv.length != 1) || !(argsv[0] instanceof String)) {
          throw new IllegalArgumentException(
              "Dynamic method get<Artefact>Class(artefactName) requires a single String parameter");
        }
        return getArtefact(match.group(2), argsv[0].toString());
      }

      // It's a no-param getter
      return super.invokeMethod(methodName, args);
    }

    // look for isXXXXClass(y)
    match = ISCLASS_PATTERN.matcher(methodName);
    // find match
    match.find();
    if (match.matches()) {
      if ((argsv.length != 1) || !(argsv[0] instanceof Class<?>)) {
        throw new IllegalArgumentException(
            "Dynamic method is<Artefact>Class(artefactClass) requires a single Class parameter");
      }

      return isArtefactOfType(match.group(2), (Class<?>) argsv[0]);
    }

    // look for getXXXXClasses
    match = GETCLASSESMETH_PATTERN.matcher(methodName);
    // find match
    match.find();
    if (match.matches()) {
      String artefactName = GrailsNameUtils.getClassNameRepresentation(match.group(2));
      if (artefactHandlersByName.containsKey(artefactName)) {
        return getArtefacts(match.group(2));
      }

      throw new IllegalArgumentException(
          "Dynamic method get<Artefact>Classes() called for "
              + "unrecognized artefact: "
              + match.group(2));
    }

    return super.invokeMethod(methodName, args);
  }

  /**
   * Override property access and hit on xxxxClasses to return class arrays of artefacts.
   *
   * @param propertyName The name of the property, if it ends in *Classes then match and invoke
   *     internal ArtefactHandler
   * @return All the artifacts or delegate to super.getProperty
   */
  @Override
  public Object getProperty(String propertyName) {
    // look for getXXXXClasses
    final Matcher match = GETCLASSESPROP_PATTERN.matcher(propertyName);
    // find match
    match.find();
    if (match.matches()) {
      String artefactName = GrailsNameUtils.getClassNameRepresentation(match.group(1));
      if (artefactHandlersByName.containsKey(artefactName)) {
        return getArtefacts(artefactName);
      }
    }
    return super.getProperty(propertyName);
  }

  public void initialise() {
    // get all the classes that were loaded
    if (log.isDebugEnabled()) {
      log.debug("loaded classes: [" + loadedClasses + "]");
    }
    Class<?>[] classes = populateAllClasses();
    configureLoadedClasses(classes);
    initialised = true;
  }

  public boolean isInitialised() {
    return initialised;
  }

  public Metadata getMetadata() {
    return applicationMeta;
  }

  public GrailsClass getArtefactByLogicalPropertyName(String type, String logicalName) {
    ArtefactInfo info = getArtefactInfo(type);
    return info == null ? null : info.getGrailsClassByLogicalPropertyName(logicalName);
  }

  public void addArtefact(@SuppressWarnings("rawtypes") Class artefact) {
    for (ArtefactHandler artefactHandler : artefactHandlers) {
      if (artefactHandler.isArtefact(artefact)) {
        addArtefact(artefactHandler.getType(), artefact);
      }
    }
  }

  public boolean isWarDeployed() {
    return getMetadata().isWarDeployed();
  }

  public void setBeanClassLoader(ClassLoader classLoader) {
    // do nothing
  }

  public void addOverridableArtefact(@SuppressWarnings("rawtypes") Class artefact) {
    for (ArtefactHandler artefactHandler : artefactHandlers) {
      if (artefactHandler.isArtefact(artefact)) {
        addOverridableArtefact(artefactHandler.getType(), artefact);
      }
    }
  }

  public void configChanged() {
    ConfigObject co = getConfig();
    // not thread safe
    flatConfig = co.flatten();
    final ArtefactHandler[] handlers = getArtefactHandlers();
    for (ArtefactHandler handler : handlers) {
      if (handler instanceof GrailsConfigurationAware) {
        ((GrailsConfigurationAware) handler).setConfiguration(co);
      }
    }
  }

  /**
   * Adds an artefact of the given type for the given Class.
   *
   * @param artefactType The type of the artefact as defined by a ArtefactHandler instance
   * @param artefactClass A Class instance that matches the type defined by the ArtefactHandler
   * @return The GrailsClass if successful or null if it couldn't be added
   * @throws GrailsConfigurationException If the specified Class is not the same as the type defined
   *     by the ArtefactHandler
   * @see org.codehaus.groovy.grails.commons.ArtefactHandler
   */
  public GrailsClass addOverridableArtefact(
      String artefactType, @SuppressWarnings("rawtypes") Class artefactClass) {
    return addArtefact(artefactType, artefactClass, true);
  }

  protected GrailsClass addArtefact(
      String artefactType, Class<?> artefactClass, boolean overrideable) {
    ArtefactHandler handler = artefactHandlersByName.get(artefactType);
    if (handler.isArtefact(artefactClass)) {
      GrailsClass artefactGrailsClass = handler.newArtefactClass(artefactClass);
      artefactGrailsClass.setGrailsApplication(this);

      // Store the GrailsClass in cache
      DefaultArtefactInfo info = getArtefactInfo(artefactType, true);
      if (overrideable) {
        info.addOverridableGrailsClass(artefactGrailsClass);
      } else {
        info.addGrailsClass(artefactGrailsClass);
      }
      info.updateComplete();

      addToLoaded(artefactClass);

      if (isInitialised()) {
        initializeArtefacts(artefactType);
      }

      return artefactGrailsClass;
    }

    throw new GrailsConfigurationException(
        "Cannot add "
            + artefactType
            + " class ["
            + artefactClass
            + "]. It is not a "
            + artefactType
            + "!");
  }
}
 /**
  * Modifies the application's metadata, as stored in the "application.properties" file. If it
  * doesn't exist, the file is created.
  */
 public void updateMetadata(@SuppressWarnings("rawtypes") Map entries) {
   @SuppressWarnings("hiding")
   Metadata metadata = Metadata.getCurrent();
   updateMetadata(metadata, entries);
 }
 public String getAppGrailsVersion() {
   return metadata.getGrailsVersion();
 }
 public String getGrailsAppVersion() {
   return metadata.getApplicationVersion();
 }