/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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.apache.ivyde.common.ivysettings; import java.io.File; import java.lang.reflect.Method; import java.net.URLClassLoader; import java.util.Arrays; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.apache.ivy.Ivy; import org.apache.ivy.plugins.conflict.ConflictManager; import org.apache.ivy.plugins.latest.LatestStrategy; import org.apache.ivy.plugins.lock.LockStrategy; import org.apache.ivy.plugins.namespace.Namespace; import org.apache.ivy.plugins.parser.ModuleDescriptorParser; import org.apache.ivy.plugins.report.ReportOutputter; import org.apache.ivy.plugins.resolver.DependencyResolver; import org.apache.ivy.plugins.trigger.Trigger; import org.apache.ivy.plugins.version.VersionMatcher; import org.apache.ivy.util.StringUtils; import org.apache.ivyde.common.model.IvyBooleanTagAttribute; import org.apache.ivyde.common.model.IvyFile; import org.apache.ivyde.common.model.IvyModel; import org.apache.ivyde.common.model.IvyModelSettings; import org.apache.ivyde.common.model.IvyReferenceTag; import org.apache.ivyde.common.model.IvyTag; import org.apache.ivyde.common.model.IvyTagAttribute; public class IvySettingsModel extends IvyModel { private String loaded = null; private File file; private ClassLoader cl; private Map typedefClasses; public IvySettingsModel(IvyModelSettings settings, File file) { super(settings); this.file = file; this.cl = Ivy.class.getClassLoader(); this.typedefClasses = getTypedefClasses(this.cl, IvySettingsFile.getDefaultTypedefs()); doLoadModel(); } public void refreshIfNeeded(IvyFile file) { String toLoad = getLoad(file); if (!toLoad.equals(loaded)) { doRefresh(file, toLoad); } } private String getLoad(IvyFile file) { IvySettingsFile sfile = (IvySettingsFile) file; return Arrays.asList(sfile.getClasspathUrls()) + "|" + sfile.getTypedefs(); } private void doRefresh(IvyFile file, String toLoad) { IvySettingsFile sfile = (IvySettingsFile) file; clearModel(); cl = getClassLoader(sfile); typedefClasses = getTypedefClasses(sfile, cl); doLoadModel(); loaded = toLoad; } private void doLoadModel() { IvyTag ivyTag = new IvyTag("ivysettings", "Root tag of Ivy settings file"); ivyTag.addChildIvyTag(new IvyTag("property", new IvyTagAttribute[] { new IvyTagAttribute("name", true), new IvyTagAttribute("value", true), new IvyBooleanTagAttribute("override"), })); ivyTag.addChildIvyTag(new IvyTag("properties", new IvyTagAttribute[] { new IvyTagAttribute("file", true), new IvyTagAttribute("environment"), new IvyBooleanTagAttribute("override"), })); ivyTag.addChildIvyTag(new IvyTag("settings", new IvyTagAttribute[] { new IvyTagAttribute("defaultResolver"), new IvyTagAttribute("defaultLatestStrategy"), new IvyTagAttribute("defaultConflictManager"), new IvyTagAttribute("defaultBranch"), new IvyTagAttribute("defaultResolveMode"), new IvyTagAttribute("circularDependencyStrategy"), new IvyBooleanTagAttribute("validate"), new IvyBooleanTagAttribute("useRemoteConfig"), })); ivyTag.addChildIvyTag(new IvyTag("include", new IvyTagAttribute[] { new IvyTagAttribute("url"), new IvyTagAttribute("file"), })); ivyTag.addChildIvyTag(new IvyTag("classpath", new IvyTagAttribute[] { new IvyTagAttribute("url"), new IvyTagAttribute("file"), })); ivyTag.addChildIvyTag(new IvyTag("typedef", new IvyTagAttribute[] { new IvyTagAttribute("name"), new IvyTagAttribute("classname"), })); IvyTag tag = new IvyTag("locking-strategies"); addTypedefChildren(tag, getChildClasses(typedefClasses, LockStrategy.class)); ivyTag.addChildIvyTag(tag); ivyTag.addChildIvyTag(new IvyTag("caches", new IvyTagAttribute[] { new IvyTagAttribute("default"), new IvyTagAttribute("defaultCacheDir"), new IvyTagAttribute("resolutionCacheDir"), new IvyTagAttribute("repositoryCacheDir"), new IvyTagAttribute("ivyPattern"), new IvyTagAttribute("artifactPattern"), new IvyBooleanTagAttribute("checkUpToDate"), new IvyBooleanTagAttribute("useOrigin"), new IvyTagAttribute("lockStrategy"), }).addChildIvyTag(new IvyTag("cache", new IvyTagAttribute[] { new IvyTagAttribute("name"), new IvyTagAttribute("basedir"), new IvyTagAttribute("ivyPattern"), new IvyTagAttribute("artifactPattern"), new IvyBooleanTagAttribute("useOrigin"), new IvyTagAttribute("lockStrategy"), new IvyTagAttribute("defaultTTL"), }).addChildIvyTag(new IvyTag("ttl", new IvyTagAttribute[] { new IvyTagAttribute("organisation"), new IvyTagAttribute("module"), new IvyTagAttribute("revision"), new IvyTagAttribute("matcher"), new IvyTagAttribute("duration", true), })))); tag = new IvyTag("latest-strategies"); addTypedefChildren(tag, getChildClasses(typedefClasses, LatestStrategy.class)); ivyTag.addChildIvyTag(tag); tag = new IvyTag("parsers"); addTypedefChildren(tag, getChildClasses(typedefClasses, ModuleDescriptorParser.class)); ivyTag.addChildIvyTag(tag); tag = new IvyTag("namespaces"); addTypedefChildren(tag, getChildClasses(typedefClasses, Namespace.class)); ivyTag.addChildIvyTag(tag); tag = new IvyTag("macrodef", new IvyTagAttribute[] { new IvyTagAttribute("name"), }).addChildIvyTag(new IvyTag("attribute", new IvyTagAttribute[] { new IvyTagAttribute("name"), new IvyTagAttribute("default"), })); addTypedefChildren(tag, getChildClasses(typedefClasses, DependencyResolver.class)); ivyTag.addChildIvyTag(tag); tag = new IvyTag("resolvers"); addTypedefChildren(tag, getChildClasses(typedefClasses, DependencyResolver.class)); tag.addChildIvyTag(new IvyReferenceTag("resolver")); ivyTag.addChildIvyTag(tag); tag = new IvyTag("conflict-managers"); addTypedefChildren(tag, getChildClasses(typedefClasses, ConflictManager.class)); ivyTag.addChildIvyTag(tag); ivyTag.addChildIvyTag(new IvyTag("modules") .addChildIvyTag(new IvyTag("module", new IvyTagAttribute[] { new IvyTagAttribute("organisation"), new IvyTagAttribute("name"), new IvyTagAttribute("revision"), new IvyTagAttribute("matcher"), new IvyTagAttribute("resolver"), new IvyTagAttribute("conflict-manager"), new IvyTagAttribute("branch"), new IvyTagAttribute("resolveMode"), }))); tag = new IvyTag("outputters"); addTypedefChildren(tag, getChildClasses(typedefClasses, ReportOutputter.class)); ivyTag.addChildIvyTag(tag); ivyTag.addChildIvyTag(new IvyTag("statuses", new IvyTagAttribute[] { new IvyTagAttribute("default"), }) .addChildIvyTag(new IvyTag("status", new IvyTagAttribute[] { new IvyTagAttribute("name"), new IvyTagAttribute("integration"), }))); tag = new IvyTag("triggers"); addTypedefChildren(tag, getChildClasses(typedefClasses, Trigger.class)); ivyTag.addChildIvyTag(tag); tag = new IvyTag("version-matchers"); addTypedefChildren(tag, getChildClasses(typedefClasses, VersionMatcher.class)); ivyTag.addChildIvyTag(tag); addTag(ivyTag); } private void addTypedefChildren(IvyTag tag, Map children) { for (Iterator it = children.entrySet().iterator(); it.hasNext();) { Entry entry = (Entry) it.next(); Class clazz = (Class) entry.getValue(); String childName = (String) entry.getKey(); tag.addChildIvyTag(typedefedTag(childName, clazz)); } } private IvyTag typedefedTag(String tagName, final Class clazz) { IvyTag child = new IvyTag(tagName) { // we lazy load children, since we may have a loop in children (chain can contain chain) // causing a stack overflow if we try to recursively add typedefed children private boolean init = false; public List getAttributes() { init(); return super.getAttributes(); } public List getChilds() { init(); return super.getChilds(); } public boolean hasChild() { init(); return super.hasChild(); } private void init() { if (!init) { try { Method[] methods = clazz.getMethods(); for (int i = 0; i < methods.length; i++) { Method m = methods[i]; if (m.getName().startsWith("create") && m.getParameterTypes().length == 0 && isSupportedChildType(m.getReturnType())) { String name = StringUtils .uncapitalize(m.getName().substring("create".length())); if (name.length() == 0) { continue; } addChildIvyTag(typedefedTag(name, m.getReturnType())); } else if (m.getName().startsWith("add") && m.getParameterTypes().length == 1 && isSupportedChildType(m.getParameterTypes()[0]) && Void.TYPE.equals(m.getReturnType())) { String name = StringUtils.uncapitalize(m.getName().substring( m.getName().startsWith("addConfigured") ? "addConfigured" .length() : "add".length())); if (name.length() == 0) { addTypedefChildren(this, getChildClasses(typedefClasses, m .getParameterTypes()[0])); } else { addChildIvyTag(typedefedTag(name, m.getParameterTypes()[0])); } } else if (m.getName().startsWith("set") && Void.TYPE.equals(m.getReturnType()) && m.getParameterTypes().length == 1 && isSupportedAttributeType(m.getParameterTypes()[0])) { IvyTagAttribute att = new IvyTagAttribute(StringUtils .uncapitalize(m.getName().substring("set".length()))); if (m.getParameterTypes()[0] == boolean.class) { att.setValueProvider(IvyBooleanTagAttribute.VALUE_PROVIDER); } addAttribute(att); } } } catch (NoClassDefFoundError e) { // we catch this because listing methods may raise a NoClassDefFoundError // if the class relies on a dependency which is not available in classpath getSettings().logError( "impossible to init tag for " + clazz + ": " + e, null); } catch (Exception e) { getSettings().logError( "error occured while initializing tag for " + clazz + ": " + e, e); } init = true; } } }; return child; } protected boolean isSupportedChildType(Class type) { return !Void.TYPE.equals(type) && !String.class.equals(type) && !Character.class.equals(type) && !char.class.equals(type) && !Boolean.class.equals(type) && !boolean.class.equals(type) && !Integer.class.equals(type) && !int.class.equals(type) && !Short.class.equals(type) && !short.class.equals(type) && !Long.class.equals(type) && !long.class.equals(type) && !Class.class.equals(type); } private boolean isSupportedAttributeType(Class type) { if (String.class.isAssignableFrom(type) || Character.class.equals(type) || char.class.equals(type) || Boolean.class.equals(type) || boolean.class.equals(type) || Short.class.equals(type) || short.class.equals(type) || Integer.class.equals(type) || int.class.equals(type) || Long.class.equals(type) || long.class.equals(type) || Class.class.equals(type) ) { return true; } try { type.getConstructor(new Class[] {String.class}); return true; } catch (Exception e) { return false; } } private ClassLoader getClassLoader(IvySettingsFile sfile) { if (sfile.getClasspathUrls().length > 0) { return new URLClassLoader(sfile.getClasspathUrls(), Ivy.class.getClassLoader()); } else { return Ivy.class.getClassLoader(); } } private Map/*<String,Class>*/ getTypedefClasses(IvySettingsFile file, ClassLoader cl) { Map typedefs = file.getTypedefs(); return getTypedefClasses(cl, typedefs); } private Map getTypedefClasses(ClassLoader cl, Map typedefs) { Map/*<String,Class>*/ classes = new LinkedHashMap(); for (Iterator it = typedefs.entrySet().iterator(); it.hasNext();) { Entry entry = (Entry) it.next(); try { classes.put(entry.getKey(), cl.loadClass((String) entry.getValue())); } catch (ClassNotFoundException e) { // ignored } } return classes; } private Map/*<String,Class>*/ getChildClasses(Map/*<String,Class>*/ classes, Class type) { Map/*<String,Class>*/ childClasses = new LinkedHashMap(); for (Iterator it = classes.entrySet().iterator(); it.hasNext();) { Entry entry = (Entry) it.next(); if (type.isAssignableFrom((Class) entry.getValue())) { childClasses.put(entry.getKey(), entry.getValue()); } } return childClasses; } protected String getRootIvyTagName() { return "ivysettings"; } public IvyFile newIvyFile(String name, String content, int documentOffset) { return new IvySettingsFile(getSettings(), file, name, content, documentOffset); } }