]> source.dussan.org Git - aspectj.git/commitdiff
loadtime java1.3 module
authoravasseur <avasseur>
Wed, 4 May 2005 14:42:14 +0000 (14:42 +0000)
committeravasseur <avasseur>
Wed, 4 May 2005 14:42:14 +0000 (14:42 +0000)
loadtime/build.xml [new file with mode: 0644]
loadtime/src/org/aspectj/weaver/loadtime/Aj.java [new file with mode: 0644]
loadtime/src/org/aspectj/weaver/loadtime/ClassLoaderWeavingAdaptor.java [new file with mode: 0644]
loadtime/src/org/aspectj/weaver/loadtime/ClassPreProcessor.java [new file with mode: 0644]
loadtime/src/org/aspectj/weaver/loadtime/Options.java [new file with mode: 0644]
loadtime/src/org/aspectj/weaver/loadtime/definition/Definition.java [new file with mode: 0644]
loadtime/src/org/aspectj/weaver/loadtime/definition/DocumentParser.java [new file with mode: 0644]
loadtime/testsrc/LoadtimeModuleTests.java [new file with mode: 0644]
loadtime/testsrc/org/aspectj/weaver/loadtime/test/DocumentParserTest.java [new file with mode: 0644]
loadtime/testsrc/org/aspectj/weaver/loadtime/test/simple.xml [new file with mode: 0644]
loadtime/testsrc/org/aspectj/weaver/loadtime/test/simpleWithDtd.xml [new file with mode: 0644]

diff --git a/loadtime/build.xml b/loadtime/build.xml
new file mode 100644 (file)
index 0000000..dbdfa8e
--- /dev/null
@@ -0,0 +1,62 @@
+<?xml version="1.0"?>
+<project name="loadtime" default="all" basedir=".">
+
+    <import file="../build/build-common.xml"/>
+    <import file="../asm/build.xml"/>
+    <import file="../bridge/build.xml"/>
+    <import file="../util/build.xml"/>
+    <import file="../weaver/build.xml"/>
+
+
+    <path id="loadtime.test.src.path">
+        <fileset dir="${basedir}/../lib">
+            <include name="junit/*.jar"/>
+        </fileset>
+        <path refid="loadtime.src.path"/>
+    </path>
+
+    <path id="loadtime.src.path">
+        <pathelement path="../asm/bin"/>
+        <pathelement path="../bridge/bin"/>
+        <pathelement path="../util/bin"/>
+        <pathelement path="../weaver/bin"/>
+        <fileset dir="${basedir}/../lib">
+            <include name="bcel/*.jar"/>
+        </fileset>
+    </path>
+
+    <target name="compile" depends="init,
+                                    asm.compile,
+                                    bridge.compile,
+                                    util.compile,
+                                    weaver.compile">
+        <srccompile project="loadtime" path="loadtime.src.path"/>
+        <!-- copy resources -->
+        <copy todir="../loadtime/bin">
+            <fileset dir="../loadtime/src" includes="**/*.dtd"/>
+        </copy>
+    </target>
+
+    <target name="test:compile" depends="compile">
+        <testcompile project="loadtime" path="loadtime.src.path"/>
+        <!-- copy resources -->
+        <copy todir="../loadtime/bin">
+            <fileset dir="../loadtime/testsrc" includes="**/*.xml"/>
+        </copy>
+    </target>
+
+    <target name="test" depends="test:compile">
+        <testrun project="loadtime" path="loadtime.test.src.path" suite="LoadtimeModuleTests"/>
+    </target>
+
+    <target name="jar" depends="compile">
+        <delete file="${build.ajdir}/jars/loadtime.jar"/>
+        <jar destfile="${build.ajdir}/jars/loadtime.jar">
+            <fileset dir="bin">
+                <include name="**/*"/>
+            </fileset>
+        </jar>
+    </target>
+
+</project>
+
diff --git a/loadtime/src/org/aspectj/weaver/loadtime/Aj.java b/loadtime/src/org/aspectj/weaver/loadtime/Aj.java
new file mode 100644 (file)
index 0000000..057669d
--- /dev/null
@@ -0,0 +1,145 @@
+/*******************************************************************************
+ * Copyright (c) 2005 Contributors.
+ * All rights reserved.
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution and is available at
+ * http://eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *   Alexandre Vasseur         initial implementation
+ *******************************************************************************/
+package org.aspectj.weaver.loadtime;
+
+import org.aspectj.asm.IRelationship;
+import org.aspectj.bridge.IMessage;
+import org.aspectj.bridge.ISourceLocation;
+import org.aspectj.bridge.Message;
+import org.aspectj.weaver.ICrossReferenceHandler;
+import org.aspectj.weaver.World;
+import org.aspectj.weaver.patterns.PatternParser;
+import org.aspectj.weaver.patterns.TypePattern;
+import org.aspectj.weaver.bcel.BcelWeaver;
+import org.aspectj.weaver.bcel.BcelWorld;
+import org.aspectj.weaver.loadtime.definition.Definition;
+import org.aspectj.weaver.loadtime.definition.DocumentParser;
+import org.aspectj.weaver.tools.GeneratedClassHandler;
+import org.aspectj.weaver.tools.WeavingAdaptor;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.WeakHashMap;
+
+/**
+ * Adapter between the generic class pre processor interface and the AspectJ weaver
+ * Load time weaving consistency relies on Bcel.setRepository
+ *
+ * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
+ */
+public class Aj implements ClassPreProcessor {
+
+    /**
+     * Initialization
+     */
+    public void initialize() {
+        ;
+    }
+
+    /**
+     * Weave
+     *
+     * @param className
+     * @param bytes
+     * @param loader
+     * @return
+     */
+    public byte[] preProcess(String className, byte[] bytes, ClassLoader loader) {
+        //TODO AV needs to doc that
+        if (loader == null || className == null) {
+            // skip boot loader or null classes (hibernate)
+            return bytes;
+        }
+
+        try {
+            byte[] weaved = WeaverContainer.getWeaver(loader).weaveClass(className, bytes);
+            //FIXME AV make dump optionnal and configurable
+            __dump(className, weaved);
+            return weaved;
+        } catch (Throwable t) {
+            t.printStackTrace();
+            return bytes;
+        }
+    }
+
+    /**
+     * Cache of weaver
+     * There is one weaver per classloader
+     */
+    static class WeaverContainer {
+
+        private static Map weavingAdaptors = new WeakHashMap();
+
+        static WeavingAdaptor getWeaver(ClassLoader loader) {
+            synchronized (weavingAdaptors) {
+                WeavingAdaptor weavingAdaptor = (WeavingAdaptor) weavingAdaptors.get(loader);
+                if (weavingAdaptor == null) {
+                    weavingAdaptor = new ClassLoaderWeavingAdaptor(loader);
+                    weavingAdaptors.put(loader, weavingAdaptor);
+                }
+                return weavingAdaptor;
+            }
+        }
+    }
+
+    static void defineClass(ClassLoader loader, String name, byte[] bytes) {
+        try {
+            //TODO av protection domain, and optimize
+            Method defineClass = ClassLoader.class.getDeclaredMethod(
+                    "defineClass", new Class[]{
+                        String.class, bytes.getClass(), int.class, int.class
+                    }
+            );
+            defineClass.setAccessible(true);
+            defineClass.invoke(
+                    loader, new Object[]{
+                        name,
+                        bytes,
+                        new Integer(0),
+                        new Integer(bytes.length)
+                    }
+            );
+        } catch (Throwable t) {
+            t.printStackTrace();
+        }
+    }
+
+    /**
+     * Dump the given bytcode in _dump/...
+     *
+     * @param name
+     * @param b
+     * @throws Throwable
+     */
+    static void __dump(String name, byte[] b) throws Throwable {
+        String className = name.replace('.', '/');
+        final File dir;
+        if (className.indexOf('/') > 0) {
+            dir = new File("_dump" + File.separator + className.substring(0, className.lastIndexOf('/')));
+        } else {
+            dir = new File("_dump");
+        }
+        dir.mkdirs();
+        String fileName = "_dump" + File.separator + className + ".class";
+        FileOutputStream os = new FileOutputStream(fileName);
+        os.write(b);
+        os.close();
+    }
+
+}
diff --git a/loadtime/src/org/aspectj/weaver/loadtime/ClassLoaderWeavingAdaptor.java b/loadtime/src/org/aspectj/weaver/loadtime/ClassLoaderWeavingAdaptor.java
new file mode 100644 (file)
index 0000000..2f74738
--- /dev/null
@@ -0,0 +1,269 @@
+/*******************************************************************************
+ * Copyright (c) 2005 Contributors.
+ * All rights reserved.
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution and is available at
+ * http://eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *   Alexandre Vasseur         initial implementation
+ *******************************************************************************/
+package org.aspectj.weaver.loadtime;
+
+import org.aspectj.weaver.tools.WeavingAdaptor;
+import org.aspectj.weaver.tools.GeneratedClassHandler;
+import org.aspectj.weaver.patterns.TypePattern;
+import org.aspectj.weaver.patterns.PatternParser;
+import org.aspectj.weaver.bcel.BcelWorld;
+import org.aspectj.weaver.bcel.BcelWeaver;
+import org.aspectj.weaver.ICrossReferenceHandler;
+import org.aspectj.weaver.World;
+import org.aspectj.weaver.ResolvedTypeX;
+import org.aspectj.weaver.TypeX;
+import org.aspectj.weaver.loadtime.definition.DocumentParser;
+import org.aspectj.weaver.loadtime.definition.Definition;
+import org.aspectj.bridge.ISourceLocation;
+import org.aspectj.bridge.Message;
+import org.aspectj.bridge.IMessage;
+import org.aspectj.asm.IRelationship;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+
+/**
+ * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
+ */
+public class ClassLoaderWeavingAdaptor extends WeavingAdaptor {
+
+    //ATAJ LTW include/exclude
+    private List m_includeTypePattern = new ArrayList();
+    private List m_excludeTypePattern = new ArrayList();
+    private List m_aspectExcludeTypePattern = new ArrayList();
+    public void addIncludeTypePattern(TypePattern typePattern) {
+        m_includeTypePattern.add(typePattern);
+    }
+    public void addExcludeTypePattern(TypePattern typePattern) {
+        m_excludeTypePattern.add(typePattern);
+    }
+    public void addAspectExcludeTypePattern(TypePattern typePattern) {
+        m_aspectExcludeTypePattern.add(typePattern);
+    }
+
+    public ClassLoaderWeavingAdaptor(final ClassLoader loader) {
+        super(null);// at this stage we don't have yet a generatedClassHandler to define to the VM the closures
+        this.generatedClassHandler = new GeneratedClassHandler() {
+            /**
+             * Callback when we need to define a Closure in the JVM
+             *
+             * @param name
+             * @param bytes
+             */
+            public void acceptClass(String name, byte[] bytes) {
+                //TODO av make dump configurable
+                try {
+                    Aj.__dump(name, bytes);
+                } catch (Throwable throwable) {
+                    throwable.printStackTrace();
+                }
+                Aj.defineClass(loader, name, bytes);// could be done lazily using the hook
+            }
+        };
+
+        bcelWorld = new BcelWorld(
+                loader, messageHandler, new ICrossReferenceHandler() {
+                    public void addCrossReference(ISourceLocation from, ISourceLocation to, IRelationship.Kind kind, boolean runtimeTest) {
+                        ;// for tools only
+                    }
+                }
+        );
+
+//            //TODO this AJ code will call
+//            //org.aspectj.apache.bcel.Repository.setRepository(this);
+//            //ie set some static things
+//            //==> bogus as Bcel is expected to be
+//            org.aspectj.apache.bcel.Repository.setRepository(new ClassLoaderRepository(loader));
+
+        weaver = new BcelWeaver(bcelWorld);
+
+        // register the definitions
+        registerDefinitions(weaver, loader);
+
+        // after adding aspects
+        weaver.prepareForWeave();
+    }
+
+    /**
+     * Load and cache the aop.xml/properties according to the classloader visibility rules
+     *
+     * @param weaver
+     * @param loader
+     */
+    private void registerDefinitions(final BcelWeaver weaver, final ClassLoader loader) {
+        try {
+            //TODO av underoptimized: we will parse each XML once per CL that see it
+            Enumeration xmls = loader.getResources("/META-INF/aop.xml");
+            List definitions = new ArrayList();
+
+            //TODO av dev mode needed ? TBD -Daj5.def=...
+            if (loader != null && loader != ClassLoader.getSystemClassLoader().getParent()) {
+                String file = System.getProperty("aj5.def", null);
+                if (file != null) {
+                    definitions.add(DocumentParser.parse((new File(file)).toURL()));
+                }
+            }
+
+            while (xmls.hasMoreElements()) {
+                URL xml = (URL) xmls.nextElement();
+                definitions.add(DocumentParser.parse(xml));
+            }
+            registerOptions(weaver, loader, definitions);
+            registerAspectExclude(weaver, loader, definitions);
+            registerAspects(weaver, loader, definitions);
+            registerIncludeExclude(weaver, loader, definitions);
+        } catch (Exception e) {
+            weaver.getWorld().getMessageHandler().handleMessage(
+                    new Message("Register definition failed", IMessage.WARNING, e, null)
+            );
+        }
+    }
+
+    /**
+     * Configure the weaver according to the option directives
+     * TODO av - don't know if it is that good to reuse, since we only allow a small subset of options in LTW
+     *
+     * @param weaver
+     * @param loader
+     * @param definitions
+     */
+    private void registerOptions(final BcelWeaver weaver, final ClassLoader loader, final List definitions) {
+        StringBuffer allOptions = new StringBuffer();
+        for (Iterator iterator = definitions.iterator(); iterator.hasNext();) {
+            Definition definition = (Definition) iterator.next();
+            allOptions.append(definition.getWeaverOptions()).append(' ');
+        }
+
+        Options.WeaverOption weaverOption = Options.parse(allOptions.toString(), loader);
+
+        // configure the weaver and world
+        // AV - code duplicates AspectJBuilder.initWorldAndWeaver()
+        World world = weaver.getWorld();
+        world.setMessageHandler(weaverOption.messageHandler);
+        world.setXlazyTjp(weaverOption.lazyTjp);
+        weaver.setReweavableMode(weaverOption.reWeavable, false);
+        world.setXnoInline(weaverOption.noInline);
+        world.setBehaveInJava5Way(weaverOption.java5);
+        //TODO proceedOnError option
+    }
+
+    private void registerAspectExclude(final BcelWeaver weaver, final ClassLoader loader, final List definitions) {
+        for (Iterator iterator = definitions.iterator(); iterator.hasNext();) {
+            Definition definition = (Definition) iterator.next();
+            for (Iterator iterator1 = definition.getAspectExcludePatterns().iterator(); iterator1.hasNext();) {
+                String exclude = (String) iterator1.next();
+                TypePattern excludePattern = new PatternParser(exclude).parseTypePattern();
+                m_aspectExcludeTypePattern.add(excludePattern);
+            }
+        }
+    }
+
+    /**
+     * Register the aspect, following include / exclude rules
+     *
+     * @param weaver
+     * @param loader
+     * @param definitions
+     */
+    private void registerAspects(final BcelWeaver weaver, final ClassLoader loader, final List definitions) {
+        //TODO: the exclude aspect allow to exclude aspect defined upper in the CL hierarchy - is it what we want ??
+        // if not, review the getResource so that we track which resource is defined by which CL
+
+        //it aspectClassNames
+        //exclude if in any of the exclude list
+        for (Iterator iterator = definitions.iterator(); iterator.hasNext();) {
+            Definition definition = (Definition) iterator.next();
+            for (Iterator aspects = definition.getAspectClassNames().iterator(); aspects.hasNext();) {
+                String aspectClassName = (String) aspects.next();
+                if (acceptAspect(aspectClassName)) {
+                    weaver.addLibraryAspect(aspectClassName);
+                }
+            }
+        }
+
+        //it concreteAspects
+        //exclude if in any of the exclude list
+        //TODO
+    }
+
+    /**
+     * Register the include / exclude filters
+     *
+     * @param weaver
+     * @param loader
+     * @param definitions
+     */
+    private void registerIncludeExclude(final BcelWeaver weaver, final ClassLoader loader, final List definitions) {
+        for (Iterator iterator = definitions.iterator(); iterator.hasNext();) {
+            Definition definition = (Definition) iterator.next();
+            for (Iterator iterator1 = definition.getIncludePatterns().iterator(); iterator1.hasNext();) {
+                String include = (String) iterator1.next();
+                TypePattern includePattern = new PatternParser(include).parseTypePattern();
+                m_includeTypePattern.add(includePattern);
+            }
+            for (Iterator iterator1 = definition.getExcludePatterns().iterator(); iterator1.hasNext();) {
+                String exclude = (String) iterator1.next();
+                TypePattern excludePattern = new PatternParser(exclude).parseTypePattern();
+                m_excludeTypePattern.add(excludePattern);
+            }
+        }
+    }
+
+    protected boolean accept(String className) {
+        // avoid ResolvedType if not needed
+        if (m_excludeTypePattern.isEmpty() && m_includeTypePattern.isEmpty()) {
+            return true;
+        }
+        //TODO AV - optimize for className.startWith only
+        ResolvedTypeX classInfo = weaver.getWorld().getCoreType(TypeX.forName(className));
+        //exclude
+        for (Iterator iterator = m_excludeTypePattern.iterator(); iterator.hasNext();) {
+            TypePattern typePattern = (TypePattern) iterator.next();
+            if (typePattern.matchesStatically(classInfo)) {
+                // exclude match - skip
+                return false;
+            }
+        }
+        for (Iterator iterator = m_includeTypePattern.iterator(); iterator.hasNext();) {
+            TypePattern typePattern = (TypePattern) iterator.next();
+            if (! typePattern.matchesStatically(classInfo)) {
+                // include does not match - skip
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private boolean acceptAspect(String aspectClassName) {
+        // avoid ResolvedType if not needed
+        if (m_aspectExcludeTypePattern.isEmpty()) {
+            return true;
+        }
+        //TODO AV - optimize for className.startWith only
+        ResolvedTypeX classInfo = weaver.getWorld().getCoreType(TypeX.forName(aspectClassName));
+        //exclude
+        for (Iterator iterator = m_aspectExcludeTypePattern.iterator(); iterator.hasNext();) {
+            TypePattern typePattern = (TypePattern) iterator.next();
+            if (typePattern.matchesStatically(classInfo)) {
+                // exclude match - skip
+                return false;
+            }
+        }
+        return true;
+    }
+
+}
diff --git a/loadtime/src/org/aspectj/weaver/loadtime/ClassPreProcessor.java b/loadtime/src/org/aspectj/weaver/loadtime/ClassPreProcessor.java
new file mode 100644 (file)
index 0000000..d47f141
--- /dev/null
@@ -0,0 +1,36 @@
+/*******************************************************************************
+ * Copyright (c) 2005 Contributors.
+ * All rights reserved.
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution and is available at
+ * http://eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *   Alexandre Vasseur         initial implementation
+ *******************************************************************************/
+package org.aspectj.weaver.loadtime;
+
+/**
+ * Generic class pre processor interface that allows to separate the AspectJ 5 load time weaving
+ * from Java 5 JVMTI interfaces for further use on Java 1.3 / 1.4
+ *
+ * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
+ */
+public interface ClassPreProcessor {
+
+    /**
+     * Post constructor initialization, usually empty
+     */
+    void initialize();
+
+    /**
+     * Weave
+     *
+     * @param className
+     * @param bytes
+     * @param classLoader
+     * @return
+     */
+    byte[] preProcess(String className, byte[] bytes, ClassLoader classLoader);
+}
\ No newline at end of file
diff --git a/loadtime/src/org/aspectj/weaver/loadtime/Options.java b/loadtime/src/org/aspectj/weaver/loadtime/Options.java
new file mode 100644 (file)
index 0000000..c466f97
--- /dev/null
@@ -0,0 +1,165 @@
+/*******************************************************************************
+ * Copyright (c) 2005 Contributors.
+ * All rights reserved.
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution and is available at
+ * http://eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *   Alexandre Vasseur         initial implementation
+ *******************************************************************************/
+package org.aspectj.weaver.loadtime;
+
+import org.aspectj.bridge.AbortException;
+import org.aspectj.bridge.IMessage;
+import org.aspectj.bridge.IMessageHandler;
+import org.aspectj.bridge.Message;
+import org.aspectj.util.LangUtil;
+
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * A class that hanldes LTW options.
+ * Note: AV - I choosed to not reuse AjCompilerOptions and alike since those implies too many dependancies on
+ * jdt and ajdt modules.
+ *
+ * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
+ */
+public class Options {
+
+    private static class DefaultMessageHandler implements IMessageHandler {
+
+        boolean isVerbose = false;
+        boolean showWeaveInfo = false;
+        boolean showWarn = true;
+
+        public boolean handleMessage(IMessage message) throws AbortException {
+            return SYSTEM_OUT.handleMessage(message);
+        }
+
+        public boolean isIgnoring(IMessage.Kind kind) {
+            if (kind.equals(IMessage.WEAVEINFO)) {
+                return !showWeaveInfo;
+            }
+            if (kind.isSameOrLessThan(IMessage.INFO)) {
+                return !isVerbose;
+            }
+            return !showWarn;
+        }
+
+        public void dontIgnore(IMessage.Kind kind) {
+            if (kind.equals(IMessage.WEAVEINFO)) {
+                showWeaveInfo = true;
+            } else if (kind.equals(IMessage.DEBUG)) {
+                isVerbose = true;
+            } else if (kind.equals(IMessage.WARNING)) {
+                showWarn = false;
+            }
+        }
+    }
+
+    private final static String OPTION_15 = "-1.5";
+    private final static String OPTION_lazyTjp = "-XlazyTjp";
+    private final static String OPTION_noWarn = "-nowarn";
+    private final static String OPTION_noWarnNone = "-warn:none";
+    private final static String OPTION_proceedOnError = "-proceedOnError";
+    private final static String OPTION_verbose = "-verbose";
+    private final static String OPTION_reweavable = "-Xreweavable";
+    private final static String OPTION_noinline = "-Xnoinline";
+    private final static String OPTION_showWeaveInfo = "-showWeaveInfo";
+    private final static String OPTIONVALUED_messageHolder = "-XmessageHolderClass:";//TODO rename to Handler
+
+    //FIXME dump option - dump what - dump before/after ?
+
+    public static WeaverOption parse(String options, ClassLoader laoder) {
+        // the first option wins
+        List flags = LangUtil.anySplit(options, " ");
+        Collections.reverse(flags);
+
+        WeaverOption weaverOption = new WeaverOption();
+        weaverOption.messageHandler = new DefaultMessageHandler();//default
+
+
+        // do a first round on the message handler since it will report the options themselves
+        for (Iterator iterator = flags.iterator(); iterator.hasNext();) {
+            String arg = (String) iterator.next();
+            if (arg.startsWith(OPTIONVALUED_messageHolder)) {
+                if (arg.length() > OPTIONVALUED_messageHolder.length()) {
+                    String handlerClass = arg.substring(OPTIONVALUED_messageHolder.length()).trim();
+                    try {
+                        Class handler = Class.forName(handlerClass, false, laoder);
+                        weaverOption.messageHandler = ((IMessageHandler) handler.newInstance());
+                    } catch (Throwable t) {
+                        weaverOption.messageHandler.handleMessage(
+                                new Message(
+                                        "Cannot instantiate message handler " + handlerClass,
+                                        IMessage.ERROR,
+                                        t,
+                                        null
+                                )
+                        );
+                    }
+                }
+            }
+        }
+
+        // configure the other options
+        for (Iterator iterator = flags.iterator(); iterator.hasNext();) {
+            String arg = (String) iterator.next();
+            if (arg.equals(OPTION_15)) {
+                weaverOption.java5 = true;
+            } else if (arg.equalsIgnoreCase(OPTION_lazyTjp)) {
+                weaverOption.lazyTjp = true;
+            } else if (arg.equalsIgnoreCase(OPTION_noinline)) {
+                weaverOption.noInline = true;
+            } else if (arg.equalsIgnoreCase(OPTION_noWarn) || arg.equalsIgnoreCase(OPTION_noWarnNone)) {
+                weaverOption.noWarn = true;
+            } else if (arg.equalsIgnoreCase(OPTION_proceedOnError)) {
+                weaverOption.proceedOnError = true;
+            } else if (arg.equalsIgnoreCase(OPTION_reweavable)) {
+                weaverOption.reWeavable = true;
+            } else if (arg.equalsIgnoreCase(OPTION_showWeaveInfo)) {
+                weaverOption.showWeaveInfo = true;
+            } else if (arg.equalsIgnoreCase(OPTION_verbose)) {
+                weaverOption.verbose = true;
+            } else {
+                weaverOption.messageHandler.handleMessage(
+                        new Message(
+                                "Cannot configure weaver with option " + arg + ": unknown option",
+                                IMessage.WARNING,
+                                null,
+                                null
+                        )
+                );
+            }
+        }
+
+        // refine message handler configuration
+        if (weaverOption.noWarn) {
+            weaverOption.messageHandler.dontIgnore(IMessage.WARNING);
+        }
+        if (weaverOption.verbose) {
+            weaverOption.messageHandler.dontIgnore(IMessage.DEBUG);
+        }
+        if (weaverOption.showWeaveInfo) {
+            weaverOption.messageHandler.dontIgnore(IMessage.WEAVEINFO);
+        }
+
+        return weaverOption;
+    }
+
+    public static class WeaverOption {
+        boolean java5;
+        boolean lazyTjp;
+        boolean noWarn;
+        boolean proceedOnError;
+        boolean verbose;
+        boolean reWeavable;
+        boolean noInline;
+        boolean showWeaveInfo;
+        IMessageHandler messageHandler;
+    }
+}
diff --git a/loadtime/src/org/aspectj/weaver/loadtime/definition/Definition.java b/loadtime/src/org/aspectj/weaver/loadtime/definition/Definition.java
new file mode 100644 (file)
index 0000000..acc3e5c
--- /dev/null
@@ -0,0 +1,97 @@
+/*******************************************************************************
+ * Copyright (c) 2005 Contributors.
+ * All rights reserved.
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution and is available at
+ * http://eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *   Alexandre Vasseur         initial implementation
+ *******************************************************************************/
+package org.aspectj.weaver.loadtime.definition;
+
+import org.aspectj.weaver.patterns.PatternParser;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+/**
+ * A POJO that contains raw strings from the XML (sort of XMLBean for our simple LTW DTD)
+ *
+ * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
+ */
+public class Definition {
+
+    private StringBuffer m_weaverOptions;
+
+    private List m_includePatterns;
+
+    private List m_excludePatterns;
+
+    private List m_aspectClassNames;
+
+    private List m_aspectExcludePatterns;
+
+    private List m_concreteAspects;
+
+    public Definition() {
+        m_weaverOptions = new StringBuffer();
+        m_includePatterns = new ArrayList(0);
+        m_excludePatterns = new ArrayList(0);
+        m_aspectClassNames = new ArrayList();
+        m_aspectExcludePatterns = new ArrayList(0);
+        m_concreteAspects = new ArrayList(0);
+    }
+
+    public String getWeaverOptions() {
+        return m_weaverOptions.toString();
+    }
+
+    public List getIncludePatterns() {
+        return m_includePatterns;
+    }
+
+    public List getExcludePatterns() {
+        return m_excludePatterns;
+    }
+
+    public List getAspectClassNames() {
+        return m_aspectClassNames;
+    }
+
+    public List getAspectExcludePatterns() {
+        return m_aspectExcludePatterns;
+    }
+
+    public List getConcreteAspects() {
+        return m_concreteAspects;
+    }
+
+    public static class ConcreteAspect {
+        String name;
+        String extend;
+        List pointcuts;
+
+        public ConcreteAspect(String name, String extend) {
+            this.name = name;
+            this.extend = extend;
+            this.pointcuts = new ArrayList();
+        }
+    }
+
+    public static class Pointcut {
+        String name;
+        String expression;
+        public Pointcut(String name, String expression) {
+            this.name = name;
+            this.expression = expression;
+        }
+    }
+
+    public void appendWeaverOptions(String option) {
+        m_weaverOptions.append(option.trim()).append(' ');
+    }
+
+}
diff --git a/loadtime/src/org/aspectj/weaver/loadtime/definition/DocumentParser.java b/loadtime/src/org/aspectj/weaver/loadtime/definition/DocumentParser.java
new file mode 100644 (file)
index 0000000..d78740f
--- /dev/null
@@ -0,0 +1,220 @@
+/*******************************************************************************
+ * Copyright (c) 2005 Contributors.
+ * All rights reserved.
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution and is available at
+ * http://eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *   Alexandre Vasseur         initial implementation
+ *******************************************************************************/
+package org.aspectj.weaver.loadtime.definition;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.ErrorHandler;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXNotRecognizedException;
+import org.xml.sax.SAXParseException;
+import org.xml.sax.XMLReader;
+import org.xml.sax.DTDHandler;
+import org.xml.sax.helpers.DefaultHandler;
+import org.xml.sax.helpers.XMLReaderFactory;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+
+import com.sun.org.apache.xerces.internal.impl.XMLEntityManager;
+
+/**
+ * FIXME AV - doc, concrete aspect
+ *
+ * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
+ */
+public class DocumentParser extends DefaultHandler {
+
+    /**
+     * The current DTD public id. The matching dtd will be searched as a resource.
+     */
+    private final static String DTD_PUBLIC_ID = "-//AspectJ//DTD 1.5.0//EN";
+
+    /**
+     * The DTD alias, for better user experience.
+     */
+    private final static String DTD_PUBLIC_ID_ALIAS = "-//AspectJ//DTD//EN";
+
+    /**
+     * A handler to the DTD stream so that we are only using one file descriptor
+     */
+    private final static InputStream DTD_STREAM = DocumentParser.class.getResourceAsStream("/aspectj_1_5_0.dtd");
+
+    private final static String ASPECTJ_ELEMENT = "aspectj";
+    private final static String WEAVER_ELEMENT = "weaver";
+    private final static String INCLUDE_ELEMENT = "include";
+    private final static String EXCLUDE_ELEMENT = "exclude";
+    private final static String OPTIONS_ATTRIBUTE = "options";
+    private final static String ASPECTS_ELEMENT = "aspects";
+    private final static String ASPECT_ELEMENT = "aspect";
+    private final static String CONCRETE_ASPECT_ELEMENT = "concrete-aspect";
+    private final static String NAME_ATTRIBUTE = "name";
+    private final static String EXTEND_ATTRIBUTE = "extends";
+    private final static String POINTCUT_ELEMENT = "pointcut";
+    private final static String WITHIN_ATTRIBUTE = "within";
+    private final static String EXPRESSION_ATTRIBUTE = "expression";
+
+
+    private final Definition m_definition;
+
+    private boolean m_inAspectJ;
+    private boolean m_inWeaver;
+    private boolean m_inAspects;
+
+    private Definition.ConcreteAspect m_lastConcreteAspect;
+
+    private DocumentParser() {
+        m_definition = new Definition();
+    }
+
+    public static Definition parse(final URL url) throws Exception {
+        InputStream in = null;
+        try {
+            DocumentParser parser = new DocumentParser();
+
+            XMLReader xmlReader = XMLReaderFactory.createXMLReader();
+            xmlReader.setContentHandler(parser);
+            xmlReader.setErrorHandler(parser);
+
+            try {
+                xmlReader.setFeature("http://xml.org/sax/features/validation", false);
+                xmlReader.setFeature("http://xml.org/sax/features/external-general-entities", false);
+                xmlReader.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
+            } catch (SAXNotRecognizedException e) {
+                ;//fine, the parser don't do validation
+            }
+
+            xmlReader.setEntityResolver(parser);
+            in = url.openStream();
+            xmlReader.parse(new InputSource(in));
+            return parser.m_definition;
+        } finally {
+            try {
+                in.close();
+            } catch (Throwable t) {
+                ;
+            }
+        }
+    }
+
+    public InputSource resolveEntity(String publicId, String systemId) throws IOException, SAXException {
+        if (publicId.equals(DTD_PUBLIC_ID) || publicId.equals(DTD_PUBLIC_ID_ALIAS)) {
+            InputStream in = DTD_STREAM;
+            if (in == null) {
+                return null;
+            } else {
+                return new InputSource(in);
+            }
+        } else {
+            System.err.println(
+                    "AspectJ - WARN - unknown DTD "
+                    + publicId
+                    + " - consider using "
+                    + DTD_PUBLIC_ID
+            );
+            return null;
+        }
+    }
+
+    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
+        if (ASPECT_ELEMENT.equals(qName)) {
+            String name = attributes.getValue(NAME_ATTRIBUTE);
+            if (!isNull(name)) {
+                m_definition.getAspectClassNames().add(name);
+            }
+        } else if (WEAVER_ELEMENT.equals(qName)) {
+            String options = attributes.getValue(OPTIONS_ATTRIBUTE);
+            if (!isNull(options)) {
+                m_definition.appendWeaverOptions(options);
+            }
+            m_inWeaver = true;
+        } else if (CONCRETE_ASPECT_ELEMENT.equals(qName)) {
+            String name = attributes.getValue(NAME_ATTRIBUTE);
+            String extend = attributes.getValue(EXTEND_ATTRIBUTE);
+            if (!isNull(name) && !isNull(extend)) {
+                m_lastConcreteAspect = new Definition.ConcreteAspect(name, extend);
+                m_definition.getConcreteAspects().add(m_lastConcreteAspect);
+            }
+        } else if (POINTCUT_ELEMENT.equals(qName) && m_lastConcreteAspect != null) {
+            String name = attributes.getValue(NAME_ATTRIBUTE);
+            String expression = attributes.getValue(EXPRESSION_ATTRIBUTE);
+            if (!isNull(name) && !isNull(expression)) {
+                m_lastConcreteAspect.pointcuts.add(new Definition.Pointcut(name, replaceXmlAnd(expression)));
+            }
+        } else if (ASPECTJ_ELEMENT.equals(qName)) {
+            if (m_inAspectJ) {
+                throw new SAXException("Found nested <aspectj> element");
+            }
+            m_inAspectJ = true;
+        } else if (ASPECTS_ELEMENT.equals(qName)) {
+            m_inAspects = true;
+        } else if (INCLUDE_ELEMENT.equals(qName) && m_inWeaver) {
+            String typePattern = attributes.getValue(WITHIN_ATTRIBUTE);
+            if (!isNull(typePattern)) {
+                m_definition.getIncludePatterns().add(typePattern);
+            }
+        } else if (EXCLUDE_ELEMENT.equals(qName) && m_inWeaver) {
+            String typePattern = attributes.getValue(WITHIN_ATTRIBUTE);
+            if (!isNull(typePattern)) {
+                m_definition.getExcludePatterns().add(typePattern);
+            }
+        } else if (EXCLUDE_ELEMENT.equals(qName) && m_inAspects) {
+            String typePattern = attributes.getValue(WITHIN_ATTRIBUTE);
+            if (!isNull(typePattern)) {
+                m_definition.getAspectExcludePatterns().add(typePattern);
+            }
+        } else {
+            throw new SAXException("Unknown element while parsing <aspectj> element: " + qName);
+        }
+        super.startElement(uri, localName, qName, attributes);
+    }
+
+    public void endElement(String uri, String localName, String qName) throws SAXException {
+        if (CONCRETE_ASPECT_ELEMENT.equals(qName)) {
+            m_lastConcreteAspect = null;
+        } else if (ASPECTJ_ELEMENT.equals(qName)) {
+            m_inAspectJ = false;
+        } else if (WEAVER_ELEMENT.equals(qName)) {
+            m_inWeaver = false;
+        } else if (ASPECTS_ELEMENT.equals(qName)) {
+            m_inAspects = false;
+        }
+        super.endElement(uri, localName, qName);
+    }
+
+    //TODO AV - define what we want for XML parser error - for now stderr
+    public void warning(SAXParseException e) throws SAXException {
+        super.warning(e);
+    }
+
+    public void error(SAXParseException e) throws SAXException {
+        super.error(e);
+    }
+
+    public void fatalError(SAXParseException e) throws SAXException {
+        super.fatalError(e);
+    }
+
+
+    private static String replaceXmlAnd(String expression) {
+        //TODO AV do we need to handle "..)AND" or "AND(.." ?
+        //FIXME AV Java 1.4 code - if KO, use some Strings util
+        return expression.replaceAll(" AND ", " && ");
+    }
+
+    private boolean isNull(String s) {
+        return (s == null || s.length() <= 0);
+    }
+
+
+}
diff --git a/loadtime/testsrc/LoadtimeModuleTests.java b/loadtime/testsrc/LoadtimeModuleTests.java
new file mode 100644 (file)
index 0000000..4cf2326
--- /dev/null
@@ -0,0 +1,36 @@
+/*******************************************************************************
+ * Copyright (c) 2005 Contributors.
+ * All rights reserved.
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution and is available at
+ * http://eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *   Alexandre Vasseur         initial implementation
+ *******************************************************************************/
+
+import junit.framework.TestCase;
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import junit.textui.TestRunner;
+import org.aspectj.weaver.loadtime.test.DocumentParserTest;
+
+/**
+ * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
+ */
+public class LoadtimeModuleTests extends TestCase {
+
+    public static Test suite() {
+        TestSuite suite = new TestSuite(LoadtimeModuleTests.class.getName());
+
+        suite.addTestSuite(DocumentParserTest.class);
+
+        return suite;
+    }
+
+    public static void main(String args[]) throws Throwable {
+        TestRunner.run(suite());
+    }
+
+}
diff --git a/loadtime/testsrc/org/aspectj/weaver/loadtime/test/DocumentParserTest.java b/loadtime/testsrc/org/aspectj/weaver/loadtime/test/DocumentParserTest.java
new file mode 100644 (file)
index 0000000..c0f26ab
--- /dev/null
@@ -0,0 +1,43 @@
+/*******************************************************************************
+ * Copyright (c) 2005 Contributors.
+ * All rights reserved.
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution and is available at
+ * http://eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *   Alexandre Vasseur         initial implementation
+ *******************************************************************************/
+package org.aspectj.weaver.loadtime.test;
+
+import junit.framework.TestCase;
+
+import java.net.URL;
+
+import org.aspectj.weaver.loadtime.definition.Definition;
+import org.aspectj.weaver.loadtime.definition.DocumentParser;
+
+/**
+ * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
+ */
+public class DocumentParserTest extends TestCase {
+
+    public void testSimple() throws Throwable {
+        URL url = DocumentParserTest.class.getResource("simple.xml");
+        Definition def = DocumentParser.parse(url);
+        assertEquals("-showWeaveInfo", def.getWeaverOptions().trim());
+    }
+
+    public void testSimpleWithDtd() throws Throwable {
+        URL url = DocumentParserTest.class.getResource("simpleWithDtd.xml");
+        Definition def = DocumentParser.parse(url);
+        assertEquals("-showWeaveInfo", def.getWeaverOptions().trim());
+        assertTrue(def.getAspectClassNames().contains("test.Aspect"));
+
+        assertEquals("foo..bar.Goo+", def.getIncludePatterns().get(0));
+        assertEquals("@Baz", def.getAspectExcludePatterns().get(0));
+
+    }
+
+}
diff --git a/loadtime/testsrc/org/aspectj/weaver/loadtime/test/simple.xml b/loadtime/testsrc/org/aspectj/weaver/loadtime/test/simple.xml
new file mode 100644 (file)
index 0000000..54d3dd7
--- /dev/null
@@ -0,0 +1,7 @@
+<aspectj>
+    <weaver options="-showWeaveInfo">
+    </weaver>
+    <aspects>
+        <aspect name="test.Aspect"/>
+    </aspects>
+</aspectj>
diff --git a/loadtime/testsrc/org/aspectj/weaver/loadtime/test/simpleWithDtd.xml b/loadtime/testsrc/org/aspectj/weaver/loadtime/test/simpleWithDtd.xml
new file mode 100644 (file)
index 0000000..03b77ff
--- /dev/null
@@ -0,0 +1,11 @@
+<?xml version="1.0"?>
+<!DOCTYPE aspectj PUBLIC "-//AspectJ//DTD//EN" "http://sswww.aspectj.org/dtd/aspectj_1_5_0.dtd">
+<aspectj>
+    <weaver options="-showWeaveInfo">
+        <include within="foo..bar.Goo+"/>
+    </weaver>
+    <aspects>
+        <exclude within="@Baz"/>
+        <aspect name="test.Aspect"/>
+    </aspects>
+</aspectj>