diff options
author | wisberg <wisberg> | 2002-12-16 17:09:36 +0000 |
---|---|---|
committer | wisberg <wisberg> | 2002-12-16 17:09:36 +0000 |
commit | c3300283ecc397d26ad9dfe31d1710ec45db2af0 (patch) | |
tree | e9acb7f3d33c1499975cec9ef3cc7ea151078344 | |
parent | 3cde920c3f7eb8241bf569007e25225d80b43c0f (diff) | |
download | aspectj-c3300283ecc397d26ad9dfe31d1710ec45db2af0.tar.gz aspectj-c3300283ecc397d26ad9dfe31d1710ec45db2af0.zip |
initial version
120 files changed, 16880 insertions, 0 deletions
diff --git a/bridge/src/.cvsignore b/bridge/src/.cvsignore new file mode 100644 index 000000000..a3f0b1b77 --- /dev/null +++ b/bridge/src/.cvsignore @@ -0,0 +1 @@ +*.lst diff --git a/bridge/src/org/aspectj/bridge/AbortException.java b/bridge/src/org/aspectj/bridge/AbortException.java new file mode 100644 index 000000000..241b6a8d9 --- /dev/null +++ b/bridge/src/org/aspectj/bridge/AbortException.java @@ -0,0 +1,225 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + +package org.aspectj.bridge; + +import java.io.PrintStream; +import java.io.PrintWriter; +import java.util.ArrayList; + +/** + * Signal that a process was aborted before completion. + * This may contain a structured IMessage which indicates + * why the process was aborted (e.g., the underlying exception). + * For processes using try/catch to complete a method abruptly + * but complete the process normally (e.g., a test failure + * causes the test to abort but the reporting and testing continues + * normally), use the static methods to borrow and return a "porter" + * to avoid the expense of constructing a stack trace each time. + * A porter stack trace is invalid, and it should only be used + * to convey a message. E.g., to print the stack of the + * AbortException and any contained message: + * <pre>catch (AbortException ae) { + * IMessage m = ae.getMessage(); + * if (!ae.isPorter()) ae.printStackTrace(System.err); + * Throwable thrown = ae.getThrown(); + * if (null != thrown) thrown.printStackTrace(System.err); + * }</pre> + */ +public class AbortException extends RuntimeException { // XXX move porters out, handle proxy better + + /** used when message text is null */ + public static final String NO_MESSAGE_TEXT + = "AbortException (no message)"; + + private static final ArrayList porters = new ArrayList(); + + /** + * A client may throw this rather than constructing their own + * if stack trace or message is not needed when caught. + */ + public static final AbortException ABORT + = new AbortException("ABORT"); + + /** + * Get a porter exception from the pool. + * Porter exceptions do <b>not</b> have valid stack traces. + * They are used only to avoid generating stack traces when + * using throw/catch to abruptly complete but continue. + */ + public static AbortException borrowPorter(IMessage message) { + AbortException result; + synchronized(porters) { + if (porters.size() > 0) { + result = (AbortException) porters.get(0); + } else { + result = new AbortException(); + } + } + result.setIMessage(message); + result.isPorter = true; + return result; + } + + /** + * Return (or add) a porter exception to the pool. + */ + public static void returnPorter(AbortException porter) { + synchronized(porters) { + if (porters.contains(porter)) { + throw new IllegalStateException("already have " + porter); + } else { + porters.add(porter); + } + } + } + + /** @return NO_MESSAGE_TEXT or message.getMessage() if not null */ + private static String extractMessage(IMessage message) { + if (null == message) { + return NO_MESSAGE_TEXT; + } else { + String m = message.getMessage(); + if (null == m) { + return NO_MESSAGE_TEXT; + } else { + return m; + } + } + } + + /** structured message abort */ + protected IMessage message; + + /** true if this is a porter exception - only used to hold message */ + protected boolean isPorter; + + /** abort with default String message */ + public AbortException() { + this((String) null); + } + + /** abort with message */ + public AbortException(String s) { + super(null != s ? s : NO_MESSAGE_TEXT); + this.message = null; + } + + /** abort with structured message */ + public AbortException(IMessage message) { + super(extractMessage(message)); + this.message = message; + } + + /** @return IMessage structured message, if set */ + public IMessage getIMessage() { + return message; + } + + /** + * The stack trace of a porter is invalid; it is only used + * to carry a message (which may itself have a wrapped exception). + * @return true if this exception is only to carry exception + */ + public boolean isPorter() { + return isPorter; + } + + /** @return Throwable at bottom of IMessage chain, if any */ + public Throwable getThrown() { + Throwable result = null; + IMessage m = getIMessage(); + if (null != m) { + result = m.getThrown(); + if (result instanceof AbortException) { + return ((AbortException) result).getThrown(); + } + } + return result; + } + + private void setIMessage(IMessage message) { + this.message = message; + } + + // ----------- proxy attempts + /** + * Get message for this AbortException, + * either associated explicitly as message + * or implicitly as IMessage message or + * its thrown message. + * @see java.lang.Throwable#getMessage() + */ + public String getMessage() { + String message = super.getMessage(); + if ((null == message) || (NO_MESSAGE_TEXT == message)) { + IMessage m = getIMessage(); + if (null != m) { + message = m.getMessage(); + if (null == message) { + Throwable thrown = m.getThrown(); + if (null != thrown) { + message = thrown.getMessage(); + } + } + } + if (null == message) { + message = NO_MESSAGE_TEXT; // better than nothing + } + } + return message; + } + + /** + * @see java.lang.Throwable#printStackTrace() + */ + public void printStackTrace() { + printStackTrace(System.out); + } + + /** + * Print the stack trace of any enclosed thrown + * or this otherwise. + * @see java.lang.Throwable#printStackTrace(PrintStream) + */ + public void printStackTrace(PrintStream s) { + IMessage m = getIMessage(); + Throwable thrown = (null == m? null : m.getThrown()); + if (!isPorter() || (null == thrown)) { + s.println("Message: " + m); + super.printStackTrace(s); + } else { + thrown.printStackTrace(s); + } + } + + /** + * Print the stack trace of any enclosed thrown + * or this otherwise. + * @see java.lang.Throwable#printStackTrace(PrintWriter) + */ + public void printStackTrace(PrintWriter s) { + IMessage m = getIMessage(); + Throwable thrown = (null == m? null : m.getThrown()); + if (null == thrown) { // Always print + if (isPorter()) { + s.println("(Warning porter AbortException without thrown)"); + } + s.println("Message: " + m); + super.printStackTrace(s); + } else { + thrown.printStackTrace(s); + } + } + +} diff --git a/bridge/src/org/aspectj/bridge/CountingMessageHandler.java b/bridge/src/org/aspectj/bridge/CountingMessageHandler.java new file mode 100644 index 000000000..103461ca7 --- /dev/null +++ b/bridge/src/org/aspectj/bridge/CountingMessageHandler.java @@ -0,0 +1,129 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + +package org.aspectj.bridge; + +import org.aspectj.util.LangUtil; + +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Iterator; + +/** + * Wrap an IMessageHandler to count messages handled. + * Messages being ignored by the delegate IMessageHandler are not counted. + */ +public class CountingMessageHandler implements IMessageHandler { + + public final IMessageHandler delegate; + public final CountingMessageHandler proxy; + private final Hashtable counters; + + public CountingMessageHandler(IMessageHandler delegate) { + LangUtil.throwIaxIfNull(delegate, "delegate"); + this.delegate = delegate; + this.counters = new Hashtable(); + proxy = (delegate instanceof CountingMessageHandler + ? (CountingMessageHandler) delegate + : null); + } + + /** @return delegate.handleMessage(IMessage) */ + public boolean handleMessage(IMessage message) throws AbortException { + if (null != proxy) { + return proxy.handleMessage(message); + } + if (null != message) { + IMessage.Kind kind = message.getKind(); + if (!isIgnoring(kind)) { + increment(kind); + } + } + return delegate.handleMessage(message); + } + + /** @return delegate.isIgnoring(IMessage.Kind) */ + public boolean isIgnoring(IMessage.Kind kind) { + return delegate.isIgnoring(kind); + } + + /** @return delegate.toString() */ + public String toString() { + return delegate.toString(); + } + + /** + * Return count of messages seen through this interface. + * @param kind the IMessage.Kind of the messages to count + * (if null, count all) + * @param orGreater if true, then count this kind and any + * considered greater by the ordering of + * IMessage.Kind.COMPARATOR + * @return number of messages of this kind (optionally or greater) + * @see IMessage.Kind.COMPARATOR + */ + public int numMessages(IMessage.Kind kind, boolean orGreater) { + if (null != proxy) { + return proxy.numMessages(kind, orGreater); + } + int result = 0; + if (null == kind) { + for (Enumeration enum = counters.elements(); enum.hasMoreElements();) { + result += ((IntHolder) enum.nextElement()).count; + } + } else if (!orGreater) { + result = numMessages(kind); + } else { + for (Iterator iter = IMessage.KINDS.iterator(); iter.hasNext();) { + IMessage.Kind k = (IMessage.Kind) iter.next(); + if (0 >= IMessage.Kind.COMPARATOR.compare(kind, k)) { + result += numMessages(k); + } + } + } + return result; + } + + /** + * @return true if 0 is less than + * <code>numMessages(IMessage.ERROR, true)</code> + */ + public boolean hasErrors() { + return (0 < numMessages(IMessage.ERROR, true)); + } + + private int numMessages(IMessage.Kind kind) { + if (null != proxy) { + return proxy.numMessages(kind); + } + IntHolder counter = (IntHolder) counters.get(kind); + return (null == counter ? 0 : counter.count); + } + + private void increment(IMessage.Kind kind) { + if (null != proxy) { + throw new IllegalStateException("not called when proxying"); + } + + IntHolder counter = (IntHolder) counters.get(kind); + if (null == counter) { + counter = new IntHolder(); + counters.put(kind, counter); + } + counter.count++; + } + + private static class IntHolder { + int count; + } +} diff --git a/bridge/src/org/aspectj/bridge/ICommand.java b/bridge/src/org/aspectj/bridge/ICommand.java new file mode 100644 index 000000000..22521f10e --- /dev/null +++ b/bridge/src/org/aspectj/bridge/ICommand.java @@ -0,0 +1,42 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.bridge; + +import java.util.*; + + + +/** + * Command wrapper with collecting parameter for messages. + */ +public interface ICommand { + /** + * Run command with the given options. + * @param args the String[] options for the command + * @param handler the IMessageHandler for all output from + * the command + * @return true if the command completed successfully + */ + boolean runCommand(String[] args, IMessageHandler handler); + + /** + * Rerun the command. + * + * @param handler the IMessageHandler for all output from the command + * + * @return true if the command completed successfully + */ + boolean repeatCommand(IMessageHandler handler); +} diff --git a/bridge/src/org/aspectj/bridge/IMessage.java b/bridge/src/org/aspectj/bridge/IMessage.java new file mode 100644 index 000000000..7cb22910c --- /dev/null +++ b/bridge/src/org/aspectj/bridge/IMessage.java @@ -0,0 +1,110 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.bridge; + +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +/** + * Wrap message with any associated throwable or source location. + */ +public interface IMessage { + /** no messages */ + public static final IMessage[] RA_IMessage = new IMessage[0]; + + // int values must sync with KINDS order below + public static final Kind INFO = new Kind("info", 10); + public static final Kind DEBUG = new Kind("debug", 20); + public static final Kind WARNING = new Kind("warning", 30); + public static final Kind ERROR = new Kind("error", 40); + public static final Kind FAIL = new Kind("fail", 50); + public static final Kind ABORT = new Kind("abort", 60); + // XXX prefer another Kind to act as selector for "any", + // but can't prohibit creating messages with it. + //public static final Kind ANY = new Kind("any-selector", 0); + + /** list of Kind in precedence order. 0 is less than + * IMessage.Kind.COMPARATOR.compareTo(KINDS.get(i), KINDS.get(i + 1)) + */ + public static final List KINDS = + Collections.unmodifiableList( + Arrays.asList( + new Kind[] { INFO, DEBUG, WARNING, ERROR, FAIL, ABORT })); + + /** @return non-null String with simple message */ + String getMessage(); + + /** @return the kind of this message */ + Kind getKind(); + + /** @return true if this is an error */ + boolean isError(); + + /** @return true if this is a warning */ + boolean isWarning(); + + /** @return true if this is an internal debug message */ + boolean isDebug(); + + /** @return true if this is information for the user */ + boolean isInfo(); + + /** @return true if the process is aborting */ + boolean isAbort(); // XXX ambiguous + + /** @return true if something failed */ + boolean isFailed(); + + /** @return Throwable associated with this message, or null if none */ + Throwable getThrown(); + + /** @return source location associated with this message, or null if none */ + ISourceLocation getISourceLocation(); + + public static final class Kind implements Comparable { + public static final Comparator COMPARATOR = new Comparator() { + public int compare(Object o1, Object o2) { + Kind one = (Kind) o1; + Kind two = (Kind) o2; + if (null == one) { + return (null == two ? 0 : 1); + } else if (null == two) { + return -1; + } else if (one == two) { + return 0; + } else { + return (one.precedence - two.precedence); + } + } + }; + + public int compareTo(Object other) { + return COMPARATOR.compare(this, other); + } + + private final int precedence; + private final String name; + + private Kind(String name, int precedence) { + this.name = name; + this.precedence = precedence; + } + public String toString() { + return name; + } + } +} diff --git a/bridge/src/org/aspectj/bridge/IMessageHandler.java b/bridge/src/org/aspectj/bridge/IMessageHandler.java new file mode 100644 index 000000000..76b76ec0b --- /dev/null +++ b/bridge/src/org/aspectj/bridge/IMessageHandler.java @@ -0,0 +1,69 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.bridge; + +import java.io.PrintWriter; + +/** + * Handle messages, logging and/or aborting as appropriate. + * Implementations define which messages are logged and whether + * the handler aborts the process. + * For messages that are costly to construct, clients may query + * {@link #isIgnoring(IMessage.Kind)} + * to avoid construction if the message will be ignored. + * Clients passing messages to an IMessageHandler should not + * interfere with aborts by catching AbortException unless + * otherwise required by their logic or the message handler. + */ +public interface IMessageHandler { + /** print all to System.err and throw AbortException on failure or abort messages */ + public static final IMessageHandler SYSTEM_ERR = + new MessageWriter(new PrintWriter(System.err, true), true); + + /** print all to System.out but do not throw AbortException on failure or abort messages */ + public static final IMessageHandler SYSTEM_OUT = + new MessageWriter(new PrintWriter(System.out, true), false); + + /** Throw exceptions for anything with ERROR or greater severity */ + public static final IMessageHandler THROW = + new IMessageHandler() { + public boolean handleMessage(IMessage message) { + if (message.getKind().compareTo(IMessage.ERROR) >= 0) { + throw new AbortException(message); + } else { + return SYSTEM_OUT.handleMessage(message); + } + } + public boolean isIgnoring(IMessage.Kind kind) { + return false; + } + }; + + /** + * Handle message, by reporting and/or throwing an AbortException. + * @param message the IMessage to handle - never null + * @return true if this message was handled by this handler + * @throws IllegalArgumentException if message is null + * @throws AbortException depending on handler logic. + */ + boolean handleMessage(IMessage message) throws AbortException; + + /** + * Signal clients whether this will ignore messages of a given type. + * Clients may use this to avoid constructing or sending certain messages. + * @return true if this handler is ignoring all messages of this type + */ + boolean isIgnoring(IMessage.Kind kind); +} diff --git a/bridge/src/org/aspectj/bridge/IMessageHolder.java b/bridge/src/org/aspectj/bridge/IMessageHolder.java new file mode 100644 index 000000000..cd36cb703 --- /dev/null +++ b/bridge/src/org/aspectj/bridge/IMessageHolder.java @@ -0,0 +1,70 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + +package org.aspectj.bridge; + +import java.util.List; + +/** + * Hold and query a collection of messages. + */ +public interface IMessageHolder + extends IMessageHandler { // XXX do not extend - mix instead + // XXX go to LT EQ GT GE LE rather than simple orGreater + /** value for orGreater parameter */ + public static final boolean ORGREATER = true; + + /** value for orGreater parameter */ + public static final boolean EQUAL = false; + + /** + * Tell whether this holder has any message of this kind + * (optionally or greater). + * @param kind the IMessage.Kind to check for - accept any if null + * @param orGreater if true, also any greater than the target kind + * as determined by IMessage.Kind.COMPARATOR + * @return true if this holder has any message of this kind, + * or if orGreater and any message has a greater kind, + * as determined by IMessage.Kind.COMPARATOR + */ + boolean hasAnyMessage(IMessage.Kind kind, boolean orGreater); + + /** + * Count the messages currently held by this holder. + * Pass null to get all kinds. + * @param kind the IMessage.Kind expected, or null for all messages + * @param orGreater if true, also any greater than the target kind + * as determined by IMessage.Kind.COMPARATOR + * @return number of IMessage held (now) by this holder + */ + int numMessages(IMessage.Kind kind, boolean orGreater); + + /** + * Get all messages or those of a specific kind. + * Pass null to get all kinds. + * @param kind the IMessage.Kind expected, or null for all messages + * @param orGreater if true, also get any greater than the target kind + * as determined by IMessage.Kind.COMPARATOR + * @return IMessage[] of messages of the right kind, or IMessage.NONE + */ + IMessage[] getMessages(IMessage.Kind kind, boolean orGreater); + + /** @return unmodifiable List view of underlying collection of IMessage */ + List getUnmodifiableListView(); + + /** + * Clear any messages. + * @throws UnsupportedOperationException if message list is read-only + */ + void clearMessages() throws UnsupportedOperationException; +} diff --git a/bridge/src/org/aspectj/bridge/ISourceLocation.java b/bridge/src/org/aspectj/bridge/ISourceLocation.java new file mode 100644 index 000000000..8dc343f5e --- /dev/null +++ b/bridge/src/org/aspectj/bridge/ISourceLocation.java @@ -0,0 +1,59 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.bridge; + +import java.io.File; + +/** + * Represent source location as a starting line/column and ending + * line in a source file. + * Implementations should be immutable. XXX why? + * @see org.aspectj.lang.reflect.SourceLocation + * @see org.aspectj.compiler.base.parser.SourceInfo + * @see org.aspectj.tools.ide.SourceLine + * @see org.aspectj.testing.harness.ErrorLine + */ +public interface ISourceLocation { + static final int MAX_LINE = Integer.MAX_VALUE / 2; + static final int MAX_COLUMN = MAX_LINE; + + /** non-null but empty (nonexisting) File constant */ + static final File NO_FILE = new File("ISourceLocation.NO_FILE"); + + /** signal that column is not known */ + static final int NO_COLUMN = Integer.MIN_VALUE +1; + + /** non-null but empty constant source location */ + static final ISourceLocation EMPTY = new SourceLocation(NO_FILE, 0, 0, 0); + + /** + * @return File source or NO_FILE if the implementation requires + * a non-null result or null otherwise + */ + File getSourceFile(); + + /** @return 0..MAX_LINE */ + int getLine(); + + /** + * @return int 0..MAX_COLUMN actual column + * or 0 if column input was ISourceLocation.NO_COLUMN + */ + int getColumn(); + + /** @return getLine()..MAX_LINE */ + int getEndLine(); + +} diff --git a/bridge/src/org/aspectj/bridge/Message.java b/bridge/src/org/aspectj/bridge/Message.java new file mode 100644 index 000000000..56e2275b7 --- /dev/null +++ b/bridge/src/org/aspectj/bridge/Message.java @@ -0,0 +1,166 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.bridge; + +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.lang.reflect.InvocationTargetException; + + +/** + * Implement messages. + * This implementation is immutable if ISourceLocation is immutable. + */ +public class Message implements IMessage { // XXX toString or renderer? + private final String message; + private final IMessage.Kind kind; + private final Throwable thrown; + private final ISourceLocation sourceLocation; + + /** convenience for constructing failure messages */ + public static Message fail(String message, Throwable thrown) { + return new Message(message, IMessage.FAIL, thrown, null); + } + + /** + * Create a (compiler) error or warning message + * @param message the String used as the underlying message + * @param sourceLocation the ISourceLocation, if any, associated with this message + * @param isError if true, use IMessage.ERROR; else use IMessage.WARNING + */ + public Message(String message, ISourceLocation location, boolean isError) { + this(message, (isError ? IMessage.ERROR : IMessage.WARNING), null, + location); + } + + /** + * Create a message, handling null values for message and kind + * if thrown is not null. + * @param message the String used as the underlying message + * @param kind the IMessage.Kind of message - not null + * @param thrown the Throwable, if any, associated with this message + * @param sourceLocation the ISourceLocation, if any, associated with this message + * @throws IllegalArgumentException if message is null and + * thrown is null or has a null message, or if kind is null + * and thrown is null. + */ + public Message(String message, IMessage.Kind kind, Throwable thrown, + ISourceLocation sourceLocation) { + this.message = message; + this.kind = kind; + this.thrown = thrown; + this.sourceLocation = sourceLocation; + if (null == message) { + if (null != thrown) { + message = thrown.getMessage(); + } + if (null == message) { + throw new IllegalArgumentException("null message"); + } + } + if (null == kind) { + throw new IllegalArgumentException("null kind"); + } + } + + /** @return the kind of this message */ + public IMessage.Kind getKind() { + return kind; + } + + /** @return true if kind == IMessage.ERROR */ + public boolean isError() { + return kind == IMessage.ERROR; + } + + /** @return true if kind == IMessage.WARNING */ + public boolean isWarning() { + return kind == IMessage.WARNING; + } + + /** @return true if kind == IMessage.DEBUG */ + public boolean isDebug() { + return kind == IMessage.DEBUG; + } + + /** + * @return true if kind == IMessage.INFO + */ + public boolean isInfo() { + return kind == IMessage.INFO; + } + + /** @return true if kind == IMessage.ABORT */ + public boolean isAbort() { + return kind == IMessage.ABORT; + } + + /** + * @return true if kind == IMessage.FAIL + */ + public boolean isFailed() { + return kind == IMessage.FAIL; + } + + /** @return non-null String with simple message */ + final public String getMessage() { + return message; + } + + /** @return Throwable associated with this message, or null if none */ + final public Throwable getThrown() { + return thrown; + } + + /** @return ISourceLocation associated with this message, or null if none */ + final public ISourceLocation getISourceLocation() { + return sourceLocation; + } + + public String toString() { + return Message.renderToString(this); + } + + public static String renderToString(IMessage message) { + ISourceLocation loc = message.getISourceLocation(); + String locString = (null == loc ? "" : " at " + loc); + Throwable thrown = message.getThrown(); + return message.getKind() + locString + ": " + message.getMessage() + + (null == thrown ? "" : render(thrown)); + } + + public static String render(Throwable thrown) { // XXX cf LangUtil.debugStr + if (null == thrown) return "null throwable"; + Throwable t = null; + if (thrown instanceof InvocationTargetException) { + t = ((InvocationTargetException)thrown).getTargetException(); + } else if (thrown instanceof ClassNotFoundException) { + t = ((ClassNotFoundException) thrown).getException(); + } + if (null != t) { + return render(t); + } + StringWriter buf = new StringWriter(); + PrintWriter writer = new PrintWriter(buf); + writer.println(" Message rendering thrown=" + thrown.getClass().getName()); + writer.println(thrown.getMessage()); + thrown.printStackTrace(writer); + try { buf.close(); } + catch (IOException ioe) {} + return buf.getBuffer().toString(); + } + +} diff --git a/bridge/src/org/aspectj/bridge/MessageHandler.java b/bridge/src/org/aspectj/bridge/MessageHandler.java new file mode 100644 index 000000000..3ffb1aa54 --- /dev/null +++ b/bridge/src/org/aspectj/bridge/MessageHandler.java @@ -0,0 +1,274 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + +package org.aspectj.bridge; + + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; + +/** + * This handler accumulates messages. + * To control messages accumulated, + * clients can ignore messages of a given kind, + * or install a listener/interceptor. + * The interceptor handles all messages (even null) first, + * and can halt further processing/accumlation by returning true. + * Clients can obtain messages accumulated using the get... methods. + * XXX this does not permit messages to be removed. + */ +public class MessageHandler implements IMessageHolder { + + /** messages accumulated */ + protected final ArrayList messages; + /** kinds of messages to be ignored */ + protected final ArrayList ignoring; + /** result of handleMessage(..) for messages not accumulated (ignored) */ + protected boolean handleMessageResult; + /** listener which can halt processing by returning true */ + protected IMessageHandler interceptor; + + /** + * same as MessageHandler(false) + */ + public MessageHandler() { + this(false); + } + + /** + * @param accumulateOnly the result of handleMessage + * (i.e., if true, then only accumulate messages - stop processing + */ + public MessageHandler(boolean accumulateOnly) { + messages = new ArrayList(); + ignoring = new ArrayList(); + init(accumulateOnly); + } + + /** + * Initialize this, removing any messages accumulated, + * kinds being ignored, or interceptor. + * Assume that this should return false from handleMessage(..). + */ + public void init() { init(false); } + + /** + * Initialize this, removing any messages accumulated, + * kinds being ignored, or interceptor. + * @param accumulateOnly boolean value returned from handleMessage + * after accumulating in list + */ + public void init(boolean accumulateOnly) { + handleMessageResult = accumulateOnly; + if (0 < messages.size()) { + messages.clear(); + } + if (0 < ignoring.size()) { + ignoring.clear(); + } + if (null != interceptor) { + interceptor = null; + } + } + + /** + * Clear the messages without changing other behavior. + */ + public void clearMessages() { + if (0 < messages.size()) { + messages.clear(); + } + } + + // ---------------------- IMessageHandler implementation + /** + * This implementation accumulates message. + * If an interceptor is installed and returns true (message handled), + * then processing halts and the message is not accumulated. + * @see org.aspectj.bridge.IMessageHandler#handleMessage(IMessage) + * @return true on interception or the constructor value otherwise + */ + public boolean handleMessage(IMessage message) { + if ((null != interceptor) && (interceptor.handleMessage(message))) { + return true; + } + if (null == message) { + throw new IllegalArgumentException("null message"); + } + if (!ignoring.contains(message.getKind())) { + messages.add(message); + } + return handleMessageResult; + } + + /** + * @return true if this kind has been flagged to be ignored. + * @see #ignore(IMessage.Kind) + * @see org.aspectj.bridge.IMessageHandler#isIgnoring(Kind) + */ + public boolean isIgnoring(IMessage.Kind kind) { + return ((null != kind) && (ignoring.contains(kind))); + } + + // ---------------------- end of IMessageHandler implementation + + + /** + * Set a message kind to be ignored from now on + */ + public void ignore(IMessage.Kind kind) { // XXX sync + if ((null != kind) && (!ignoring.contains(kind))) { + ignoring.add(kind); + } + } + + /** + * Remove a message kind from the list of those ignored from now on. + */ + public void dontIgnore(IMessage.Kind kind) { + if (null != kind) { + ignoring.remove(kind); + } + } + + /** + * @see org.aspectj.bridge.IMessageHolder#hasAnyMessage(Kind, boolean) + */ + public boolean hasAnyMessage(final IMessage.Kind kind, final boolean orGreater) { + if (null == kind) { + return (0 < messages.size()); + } + if (!orGreater) { + for (Iterator iter = messages.iterator(); iter.hasNext();) { + if (kind == ((IMessage) iter.next()).getKind()) { + return true; + } + } + } else { + final Comparator c = IMessage.Kind.COMPARATOR; + for (Iterator iter = messages.iterator(); iter.hasNext();) { + IMessage m = (IMessage) iter.next(); + if (0 >= c.compare(kind, m.getKind())) { + return true; + } + } + } + return false; + } + + /** + * @return number of messages accumulated of a given kind + */ + public int numMessages(IMessage.Kind kind, final boolean orGreater) { + if (null == kind) { + return messages.size(); + } + int result = 0; + if (!orGreater) { + for (Iterator iter = messages.iterator(); iter.hasNext();) { + if (kind == ((IMessage) iter.next()).getKind()) { + result++; + } + } + } else { + final Comparator c = IMessage.Kind.COMPARATOR; + for (Iterator iter = messages.iterator(); iter.hasNext();) { + IMessage m = (IMessage) iter.next(); + if (0 >= c.compare(kind, m.getKind())) { + result++; + } + } + } + return result; + } + + /** + * @see org.aspectj.bridge.IMessageHolder#getUnmodifiableListView() + */ + public List getUnmodifiableListView() { + return Collections.unmodifiableList(messages); + } + + /** + * Get all messages or those of a specific kind. + * Pass null to get all kinds. + * @param kind the IMessage.Kind expected, or null for all messages + * @return IMessage[] of messages of the right kind + */ + public IMessage[] getMessages(IMessage.Kind kind, final boolean orGreater) { + if (null == kind) { + return (IMessage[]) messages.toArray(IMessage.RA_IMessage); + } + ArrayList result = new ArrayList(); + if (!orGreater) { + for (Iterator iter = messages.iterator(); iter.hasNext();) { + IMessage m = (IMessage) iter.next(); + if (kind == m.getKind()) { + result.add(m); + } + } + } else { + final Comparator c = IMessage.Kind.COMPARATOR; + for (Iterator iter = messages.iterator(); iter.hasNext();) { + IMessage m = (IMessage) iter.next(); + if (0 >= c.compare(kind, m.getKind())) { + result.add(m); + } + } + } + if (0 == result.size()) { + return IMessage.RA_IMessage; + } + return (IMessage[]) result.toArray(IMessage.RA_IMessage); + } + + /** + * @return array of error messages, or IMessage.NONE + */ + public IMessage[] getErrors() { + return getMessages(IMessage.ERROR, false); + } + + /** + * @return array of warning messages, or IMessage.NONE + */ + public IMessage[] getWarnings() { + return getMessages(IMessage.WARNING, false); + } + + /** + * Set the interceptor which gets any message before we process it. + * @param interceptor the IMessageHandler passed the message. + * Pass null to remove the old interceptor. + */ + public void setInterceptor(IMessageHandler interceptor) { + this.interceptor = interceptor; + } + + /** + * @return String containing list of messages + */ + public String toString() { + if (0 == messages.size()) { + return "MessageHandler: no messages"; + } else { + return "MessageHandler: " + messages; + } + + } + +} diff --git a/bridge/src/org/aspectj/bridge/MessageUtil.java b/bridge/src/org/aspectj/bridge/MessageUtil.java new file mode 100644 index 000000000..13ab1a1fe --- /dev/null +++ b/bridge/src/org/aspectj/bridge/MessageUtil.java @@ -0,0 +1,968 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + +package org.aspectj.bridge; + +import org.aspectj.bridge.IMessage.Kind; +import org.aspectj.util.LangUtil; +import org.aspectj.util.LineReader; + +import java.io.File; +import java.io.OutputStream; +import java.io.PrintStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; + +/** + * Convenience API's for constructing, printing, and sending messages. + */ +public class MessageUtil { + + // ------ some constant, content-less messages + // no variants for "info" or "debug", which should always have content + public static final IMessage ABORT_NOTHING_TO_RUN + = new Message("aborting - nothing to run", IMessage.ABORT, null, null); + + public static final IMessage FAIL_INCOMPLETE + = new Message("run not completed", IMessage.FAIL, null, null); + + public static final IMessage ABORT_NOMESSAGE + = new Message("", IMessage.ABORT, null, null); + + public static final IMessage FAIL_NOMESSAGE + = new Message("", IMessage.FAIL, null, null); + + public static final IMessage ERROR_NOMESSAGE + = new Message("", IMessage.ERROR, null, null); + + public static final IMessage WARNING_NOMESSAGE + = new Message("", IMessage.WARNING, null, null); + + + /** handle abort message (ignored if handler is null) */ + public static boolean abort(IMessageHandler handler, String message) { + return ((null != handler) + && handler.handleMessage(abort(message))); + } + + /** create and handle exception message (ignored if handler is null) */ + public static boolean abort(IMessageHandler handler, String message, Throwable t) { + if (handler != null) return handler.handleMessage(abort(message, t)); + return false; + } + + /** create and handle fail message (ignored if handler is null) */ + public static boolean fail(IMessageHandler handler, String message) { + return ((null != handler) + && handler.handleMessage(fail(message))); + } + + /** create and handle fail message from reader (ignored if handler is null) */ + public static boolean fail(IMessageHandler handler, String message, LineReader reader) { + return ((null != handler) + && handler.handleMessage(fail(message, reader))); + } + + /** create and handle fail message (ignored if handler is null) */ + public static boolean fail(IMessageHandler handler, String message, Throwable thrown) { + return ((null != handler) + && handler.handleMessage(fail(message, thrown))); + } + + /** create and handle error message (ignored if handler is null) */ + public static boolean error(IMessageHandler handler, String message) { + return ((null != handler) + && handler.handleMessage(error(message))); + } + + /** create and handle warn message (ignored if handler is null) */ + public static boolean warn(IMessageHandler handler, String message) { + return ((null != handler) + && handler.handleMessage(warn(message))); + } + + /** create and handle debug message (ignored if handler is null) */ + public static boolean debug(IMessageHandler handler, String message) { + return ((null != handler) + && handler.handleMessage(debug(message))); + } + + /** create and handle info message (ignored if handler is null) */ + public static boolean info(IMessageHandler handler, String message) { + return ((null != handler) + && handler.handleMessage(info(message))); + } + + /** @return ABORT_NOMESSAGE if message is empty or IMessage otherwise */ // + public static IMessage abort(String message) { + if (LangUtil.isEmpty(message)) { + return ABORT_NOMESSAGE; + } else { + return new Message(message, IMessage.ABORT, null, null); + } + } + + /** @return abort IMessage with thrown and message o + * ABORT_NOMESSAGE if both are empty/null */ // + public static IMessage abort(String message, Throwable thrown) { + if (!LangUtil.isEmpty(message)) { + return new Message(message, IMessage.ABORT, thrown, null); + } else if (null == thrown) { + return ABORT_NOMESSAGE; + } else { + return new Message(thrown.getMessage(), IMessage.ABORT, thrown, null); + } + } + + /** @return FAIL_NOMESSAGE if message is empty or IMessage otherwise */ + public static IMessage fail(String message) { + if (LangUtil.isEmpty(message)) { + return FAIL_NOMESSAGE; + } else { + return fail(message, (LineReader) null); + } + } + + /** + * Create fail message. + * If message is empty but thrown is not, use thrown.getMessage() as the message. + * If message is empty and thrown is null, return FAIL_NOMESSAGE. + * @return FAIL_NOMESSAGE if thrown is null and message is empty + * or IMessage FAIL with message and thrown otherwise + */ + public static IMessage fail(String message, Throwable thrown) { + if (LangUtil.isEmpty(message)) { + if (null == thrown) { + return FAIL_NOMESSAGE; + } else { + return new Message(thrown.getMessage(), IMessage.FAIL, thrown, null); + } + } else { + return new Message(message, IMessage.FAIL, thrown, null); + } + } + + /** + * @return IMessage with IMessage.Kind FAIL and message as text + * and soure location from reader + */ + public static IMessage fail(String message, LineReader reader) { + ISourceLocation loc = null; + if (null == reader) { + loc = ISourceLocation.EMPTY; + } else { + int line = (null == reader ? 0 : reader.getLineNumber()); + if (0 < line) { + line = 0; + } + loc = new SourceLocation(reader.getFile(), line, line, 0); + } + return new Message(message, IMessage.FAIL, null, loc); + } + + /** @return ERROR_NOMESSAGE if message is empty or IMessage otherwise */ // + public static IMessage error(String message, ISourceLocation location) { + if (LangUtil.isEmpty(message)) { + return ERROR_NOMESSAGE; + } else { + return new Message(message, IMessage.ERROR, null, location); + } + } + + /** @return ERROR_NOMESSAGE if message is empty or IMessage otherwise */ // + public static IMessage error(String message) { + if (LangUtil.isEmpty(message)) { + return ERROR_NOMESSAGE; + } else { + return new Message(message, IMessage.ERROR, null, null); + } + } + + /** @return WARNING_NOMESSAGE if message is empty or IMessage otherwise */ // + public static IMessage warn(String message) { + if (LangUtil.isEmpty(message)) { + return WARNING_NOMESSAGE; + } else { + return new Message(message, IMessage.WARNING, null, null); + } + } + + /** @return IMessage.DEBUG message with message content */ + public static IMessage debug(String message) { + return new Message(message, IMessage.DEBUG, null, null); + } + + /** @return IMessage.INFO message with message content */ + public static IMessage info(String message) { + return new Message(message, IMessage.INFO, null, null); + } + + /** @return ISourceLocation with the current File/line of the reader */ + public static ISourceLocation makeSourceLocation(LineReader reader) { + LangUtil.throwIaxIfNull(reader, "reader"); + + int line = reader.getLineNumber(); + if (0 < line) { + line = 0; + } + return new SourceLocation(reader.getFile(), line, line, 0); + } + + // ------------------------ printing messages + /** + * Print total counts message to the print stream, starting each on a new line + * @param messageHolder + * @param out + */ + public static void printMessageCounts(PrintStream out, IMessageHolder messageHolder) { + if ((null == out) || (null == messageHolder)) { + return; + } + printMessageCounts(out, messageHolder, ""); + } + + public static void printMessageCounts(PrintStream out, IMessageHolder holder, String prefix) { + out.println(prefix + "MessageHolder: " + MessageUtil.renderCounts(holder)); + } + /** + * Print all message to the print stream, starting each on a new line + * @param messageHolder + * @param out + * @see #print(PrintStream, String, IMessageHolder, IMessageRenderer, IMessageHandler) + */ + public static void print(PrintStream out, IMessageHolder messageHolder) { + print(out, messageHolder, (String) null, (IMessageRenderer) null, (IMessageHandler) null); + } + + /** + * Print all message to the print stream, starting each on a new line, + * with a prefix. + * @param messageHolder + * @param out + * @see #print(PrintStream, String, IMessageHolder, IMessageRenderer, IMessageHandler) + */ + public static void print(PrintStream out, IMessageHolder holder, String prefix) { + print(out, holder, prefix, (IMessageRenderer) null, (IMessageHandler) null); + } + + /** + * Print all message to the print stream, starting each on a new line, + * with a prefix and using a renderer. + * @param messageHolder + * @param out + * @param renderer IMessageRender to render result - use MESSAGE_LINE if null + * @see #print(PrintStream, String, IMessageHolder, IMessageRenderer, IMessageHandler) + */ + public static void print(PrintStream out, IMessageHolder holder, String prefix, + IMessageRenderer renderer) { + print(out, holder, prefix, renderer, (IMessageHandler) null); + } + + /** + * Print all message to the print stream, starting each on a new line, + * with a prefix and using a renderer. + * The first line renders a summary: {prefix}MessageHolder: {summary} + * Each message line has the following form: + * <pre>{prefix}[{kind} {index}]: {rendering}</pre> + * (where "{index}" (length 3) is the position within + * the set of like-kinded messages, ignoring selector omissions. + * Renderers are free to render multi-line output. + * @param out the PrintStream sink - return silently if null + * @param messageHolder the IMessageHolder with the messages to print + * @param renderer IMessageRender to render result - use MESSAGE_ALL if null + * @param selector IMessageHandler to select messages to render - if null, do all non-null + */ + public static void print(PrintStream out, IMessageHolder holder, String prefix, + IMessageRenderer renderer, IMessageHandler selector) { + print(out, holder, prefix, renderer, selector, true); + } + + public static void print(PrintStream out, IMessageHolder holder, String prefix, + IMessageRenderer renderer, IMessageHandler selector, boolean printSummary) { + if ((null == out) || (null == holder)) { + return; + } + if (null == renderer) { + renderer = MESSAGE_ALL; + } + if (null == selector) { + selector = PICK_ALL; + } + if (printSummary) { + out.println(prefix + "MessageHolder: " + MessageUtil.renderCounts(holder)); + } + for (Iterator iter = IMessage.KINDS.iterator(); iter.hasNext();) { + IMessage.Kind kind = (IMessage.Kind) iter.next(); + if (!selector.isIgnoring(kind)) { + IMessage[] messages = holder.getMessages(kind, IMessageHolder.EQUAL); + for (int i = 0; i < messages.length; i++) { + if (selector.handleMessage(messages[i])) { + String label = (null == prefix + ? "" + : prefix + "[" + kind + " " + LangUtil.toSizedString(i, 3) + "]: "); + out.println(label + renderer.renderToString(messages[i])); + } + } + } + } + } + + public static String toShortString(IMessage message) { + if (null == message) { + return "null"; + } + String m = message.getMessage(); + Throwable t = message.getThrown(); + + return (message.getKind() + (null == m ? "" : ": " + m) + + (null == t ? "" : ": " + LangUtil.unqualifiedClassName(t))); + } + + /** @return int number of message of this kind (optionally or greater) in list */ + public static int numMessages(ArrayList messages, Kind kind, boolean orGreater) { + if (LangUtil.isEmpty(messages)) { + return 0; + } + IMessageHandler selector = makeSelector(kind, orGreater, null); + IMessage[] result = visitMessages(messages, selector, true, false); + return result.length; + } + + + /** @return unmodifiable list of IMessage complying with parameters */ + public static List getMessages(IMessageHolder holder, + IMessage.Kind kind, boolean orGreater, String infix) { // XXX untested + if (null == holder) { + return Collections.EMPTY_LIST; + } + if ((null == kind) && LangUtil.isEmpty(infix)) { + return holder.getUnmodifiableListView(); + } + IMessageHandler selector = makeSelector(kind, orGreater, infix); + IMessage[] messages = visitMessages(holder, selector, true, false); + if (LangUtil.isEmpty(messages)) { + return Collections.EMPTY_LIST; + } + return Collections.unmodifiableList(Arrays.asList(messages)); + } + + /** + * Extract messages of type kind from the input list. + * + * @param messages if null, return EMPTY_LIST + * @param kind if null, return messages + * @see MessageHandler#getMessages(Kind) + */ + public static List getMessages(List messages, IMessage.Kind kind) { + if (null == messages) { + return Collections.EMPTY_LIST; + } + if (null == kind) { + return messages; + } + ArrayList result = new ArrayList(); + for (Iterator iter = messages.iterator(); iter.hasNext();) { + IMessage element = (IMessage) iter.next(); + if (kind == element.getKind()) { + result.add(element); + } + } + if (0 == result.size()) { + return Collections.EMPTY_LIST; + } + return result; + } + + /** + * Map to the kind of messages associated with this string key. + * @param kind the String representing the kind of message (IMessage.Kind.toString()) + * @return Kind the associated IMessage.Kind, or null if not found + */ + public static IMessage.Kind getKind(String kind) { + if (null != kind) { + kind = kind.toLowerCase(); + for (Iterator iter = IMessage.KINDS.iterator(); iter.hasNext();) { + IMessage.Kind k = (IMessage.Kind) iter.next(); + if (kind.equals(k.toString())) { + return k; + } + } + } + return null; + } + + /** + * Run visitor over the set of messages in holder, + * optionally accumulating those accepted by the visitor + */ + public static IMessage[] visitMessages(IMessageHolder holder, + IMessageHandler visitor, boolean accumulate, boolean abortOnFail) { + if (null == holder) { + return IMessage.RA_IMessage; + } else { + return visitMessages(holder.getUnmodifiableListView(), visitor, accumulate, abortOnFail); + } + } + + /** + * Run visitor over the set of messages in holder, + * optionally accumulating those accepted by the visitor + */ + public static IMessage[] visitMessages(IMessage[] messages, + IMessageHandler visitor, boolean accumulate, boolean abortOnFail) { + if (LangUtil.isEmpty(messages)) { + return IMessage.RA_IMessage; + } else { + return visitMessages(Arrays.asList(messages), visitor, accumulate, abortOnFail); + } + } + + /** + * Run visitor over a collection of messages, + * optionally accumulating those accepted by the visitor + * @param messages if null or empty, return IMessage.RA_IMessage + * @param visitor run visitor.handleMessage(message) on each + * message - if null and messages not empty, IllegalArgumentException + * @param accumulate if true, then return accepted IMessage[] + * @param abortOnFail if true and visitor returns false, stop visiting + * @return IMessage.RA_IMessage if collection is empty, if not accumulate, + * or if visitor accepted no IMessage, + * or IMessage[] of accepted messages otherwise + * @throws IllegalArgumentException if any in collection are not instanceof IMessage + */ + public static IMessage[] visitMessages(Collection /*IMessage*/ messages, + IMessageHandler visitor, final boolean accumulate, final boolean abortOnFail) { + if (LangUtil.isEmpty(messages)) { + return IMessage.RA_IMessage; + } + LangUtil.throwIaxIfNull(visitor, "visitor"); + ArrayList result = (accumulate ? new ArrayList() : null); + for (Iterator iter = messages.iterator(); iter.hasNext();) { + Object o = iter.next(); + LangUtil.throwIaxIfFalse(o instanceof IMessage, "expected IMessage, got " + o); + IMessage m = (IMessage) o; + if (visitor.handleMessage(m)) { + if (accumulate) { + result.add(m); + } + } else if (abortOnFail) { + break; + } + } + if (!accumulate || (0 == result.size())) { + return IMessage.RA_IMessage; + } else { + return (IMessage[]) result.toArray(IMessage.RA_IMessage); + } + } + + /** + * Make an IMessageHandler that handles IMessage if they have the right kind + * (or greater) and contain some infix String. + * @param kind the IMessage.Kind required of the message + * @param orGreater if true, also accept messages with greater kinds, as + * defined by IMessage.Kind.COMPARATOR + * @param infix the String text to require in the message - may be null or empty + * to accept any message with the specified kind. + * @return IMessageHandler selector that works to param specs + */ + public static IMessageHandler makeSelector(IMessage.Kind kind, boolean orGreater, String infix) { + if (!orGreater && LangUtil.isEmpty(infix)) { + if (kind == IMessage.ABORT) { + return PICK_ABORT; + } else if (kind == IMessage.DEBUG) { + return PICK_DEBUG; + } else if (kind == IMessage.DEBUG) { + return PICK_DEBUG; + } else if (kind == IMessage.ERROR) { + return PICK_ERROR; + } else if (kind == IMessage.FAIL) { + return PICK_FAIL; + } else if (kind == IMessage.INFO) { + return PICK_INFO; + } else if (kind == IMessage.WARNING) { + return PICK_WARNING; + } + } + return new KindSelector(kind, orGreater, infix); + } + + // ------------------ visitors to select messages + public static final IMessageHandler PICK_ALL = new KindSelector((IMessage.Kind) null); + public static final IMessageHandler PICK_ABORT = new KindSelector(IMessage.ABORT); + public static final IMessageHandler PICK_DEBUG = new KindSelector(IMessage.DEBUG); + public static final IMessageHandler PICK_ERROR = new KindSelector(IMessage.ERROR); + public static final IMessageHandler PICK_FAIL = new KindSelector(IMessage.FAIL); + public static final IMessageHandler PICK_INFO = new KindSelector(IMessage.INFO); + public static final IMessageHandler PICK_WARNING = new KindSelector(IMessage.WARNING); + public static final IMessageHandler PICK_ABORT_PLUS = new KindSelector(IMessage.ABORT, true); + public static final IMessageHandler PICK_DEBUG_PLUS = new KindSelector(IMessage.DEBUG, true); + public static final IMessageHandler PICK_ERROR_PLUS = new KindSelector(IMessage.ERROR, true); + public static final IMessageHandler PICK_FAIL_PLUS = new KindSelector(IMessage.FAIL, true); + public static final IMessageHandler PICK_INFO_PLUS = new KindSelector(IMessage.INFO, true); + public static final IMessageHandler PICK_WARNING_PLUS = new KindSelector(IMessage.WARNING, true); + + + /** implementation for PICK_... constants */ + private static class KindSelector implements IMessageHandler { + final IMessage.Kind sought; + final boolean floor; + final String infix; + + KindSelector(IMessage.Kind sought) { + this(sought, false); + } + + KindSelector(IMessage.Kind sought, boolean floor) { + this(sought, floor, null); + } + + KindSelector(IMessage.Kind sought, boolean floor, String infix) { + this.sought = sought; + this.floor = floor; + this.infix = (LangUtil.isEmpty(infix) ? null : infix); + } + + /** @return false if this message is null, + * of true if we seek any kind (null) + * or if this has the exact kind we seek + * and this has any text sought + */ + public boolean handleMessage(IMessage message) { + return ((null != message) && !isIgnoring(message.getKind()) + && textIn(message)); + } + + /** @return true if handleMessage would return false for a message of this kind */ + public boolean isIgnoring(IMessage.Kind kind) { + if (!floor) { + return ((null != sought) && (sought != kind)); + } else if (null == sought) { + return false; + } else { + return (0 < IMessage.Kind.COMPARATOR.compare(sought, kind)); + } + } + + private boolean textIn(IMessage message) { + if (null == infix) { + return true; + } + String text = message.getMessage(); + return ((null != message) && (-1 != text.indexOf(infix))); + } + } + + // ------------------ components to render messages + /** parameterize rendering behavior for messages */ + public static interface IMessageRenderer { + String renderToString(IMessage message); + } + + + /** render message more verbosely if it is worse */ + public static final IMessageRenderer MESSAGE_SCALED = new IMessageRenderer() { + public String toString() { return "MESSAGE_SCALED"; } + public String renderToString(IMessage message) { + if (null == message) { + return "((IMessage) null)"; + } + IMessage.Kind kind = message.getKind(); + int level = 3; + if ((kind == IMessage.ABORT) || (kind == IMessage.FAIL)) { + level = 1; + } else if ((kind == IMessage.ERROR) || (kind == IMessage.WARNING)) { + level = 2; + } else { + level = 3; + } + String result = null; + switch (level) { + case (1) : + result = MESSAGE_TOSTRING.renderToString(message); + break; + case (2) : + result = MESSAGE_LINE.renderToString(message); + break; + case (3) : + result = MESSAGE_SHORT.renderToString(message); + break; + } + Throwable thrown = message.getThrown(); + if (null != thrown) { + if (level == 3) { + result += "Thrown: \n" + LangUtil.renderExceptionShort(thrown); + } else { + result += "Thrown: \n" + LangUtil.renderException(thrown); + } + } + + return result; + } + }; + + /** render message as label, i.e., less than 33 char */ + public static final IMessageRenderer MESSAGE_LABEL = new IMessageRenderer() { + public String toString() { return "MESSAGE_LABEL"; } + public String renderToString(IMessage message) { + if (null == message) { + return "((IMessage) null)"; + } + return renderMessageLine(message, 5, 5, 32); + } + }; + + /** render message as label, i.e., less than 33 char, with no source location */ + public static final IMessageRenderer MESSAGE_LABEL_NOLOC = new IMessageRenderer() { + public String toString() { return "MESSAGE_LABEL_NOLOC"; } + public String renderToString(IMessage message) { + if (null == message) { + return "((IMessage) null)"; + } + return renderMessageLine(message, 10, 0, 32); + } + }; + + /** render message as line, i.e., less than 75 char, no internal line sep */ + public static final IMessageRenderer MESSAGE_LINE = new IMessageRenderer() { + public String toString() { return "MESSAGE_LINE"; } + public String renderToString(IMessage message) { + if (null == message) { + return "((IMessage) null)"; + } + return renderMessageLine(message, 8, 2, 74); + } + }; + + /** render message as line, i.e., less than 75 char, no internal line sep, + * trying to trim text as needed to end with a full source location */ + public static final IMessageRenderer MESSAGE_LINE_FORCE_LOC = new IMessageRenderer() { + public String toString() { return "MESSAGE_LINE_FORCE_LOC"; } + public String renderToString(IMessage message) { + if (null == message) { + return "((IMessage) null)"; + } + return renderMessageLine(message, 2, 40, 74); + } + }; + + /** render message without restriction, up to 10K, including throwable */ + public static final IMessageRenderer MESSAGE_ALL = new IMessageRenderer() { + public String toString() { return "MESSAGE_ALL"; } + public String renderToString(IMessage message) { + return renderMessage(message); + } + }; + +// /** render message without restriction, up to 10K, including (but eliding) throwable */ +// public static final IMessageRenderer MESSAGE_ALL_ELIDED= new IMessageRenderer() { +// public String toString() { return "MESSAGE_ALL_ELIDED"; } +// public String renderToString(IMessage message) { +// return renderMessage(message, true); +// } +// }; + + /** render message without restriction, except any Throwable thrown */ + public static final IMessageRenderer MESSAGE_MOST = new IMessageRenderer() { + public String toString() { return "MESSAGE_MOST"; } + public String renderToString(IMessage message) { + if (null == message) { + return "((IMessage) null)"; + } + return renderMessageLine(message, 1, 1, 10000); + } + }; + + /** render message as wide line, i.e., less than 256 char, no internal line sep, + * except any Throwable thrown + */ + public static final IMessageRenderer MESSAGE_WIDELINE = new IMessageRenderer() { + public String toString() { return "MESSAGE_WIDELINE"; } + public String renderToString(IMessage message) { + if (null == message) { + return "((IMessage) null)"; + } + return renderMessageLine(message, 8, 2, 255); // XXX revert to 256 + } + }; + /** render message using its toString() or "((IMessage) null)" */ + public static final IMessageRenderer MESSAGE_TOSTRING = new IMessageRenderer() { + public String toString() { return "MESSAGE_TOSTRING"; } + public String renderToString(IMessage message) { + if (null == message) { + return "((IMessage) null)"; + } + return message.toString(); + } + }; + + /** render message using toShortString(IMessage)" */ + public static final IMessageRenderer MESSAGE_SHORT = new IMessageRenderer() { + public String toString() { return "MESSAGE_SHORT"; } + public String renderToString(IMessage message) { + return toShortString(message); + } + }; + + /** + * This renders IMessage as String, ignoring empty elements + * and eliding any thrown stack traces. + * @return "((IMessage) null)" if null or String rendering otherwise, + * including everything (esp. throwable stack trace) + * @see renderSourceLocation(ISourceLocation loc) + */ + public static String renderMessage(IMessage message) { + return renderMessage(message, true); + } + + /** + * This renders IMessage as String, ignoring empty elements + * and eliding any thrown. + * @return "((IMessage) null)" if null or String rendering otherwise, + * including everything (esp. throwable stack trace) + * @see renderSourceLocation(ISourceLocation loc) + */ + public static String renderMessage(IMessage message, boolean elide) { + if (null == message) { + return "((IMessage) null)"; + } + StringBuffer result = new StringBuffer(); + + result.append(message.getKind().toString()); + result.append(" "); + + String messageString = message.getMessage(); + if (!LangUtil.isEmpty(messageString)) { + result.append(messageString); + result.append(" "); + } + + ISourceLocation loc = message.getISourceLocation(); + if ((null != loc) && (loc != ISourceLocation.EMPTY)) { + result.append("at " + renderSourceLocation(loc)); + } + Throwable thrown = message.getThrown(); + if (null != thrown) { + result.append(" -- " + LangUtil.renderExceptionShort(thrown)); + result.append("\n" + LangUtil.renderException(thrown, elide)); + } + return result.toString(); + } + + /** + * Render ISourceLocation to String, ignoring empty elements + * (null or ISourceLocation.NO_FILE or ISourceLocation.NO_COLUMN + * (though implementations may return 0 from getColumn() when + * passed NO_COLUMN as input)). + * @return "((ISourceLocation) null)" if null or String rendering + * <pre>{file:}line{:column}</pre> + * + */ + public static String renderSourceLocation(ISourceLocation loc) { + if (null == loc) { + return "((ISourceLocation) null)"; + } + StringBuffer sb = new StringBuffer(); + + File sourceFile = loc.getSourceFile(); + if (sourceFile != ISourceLocation.NO_FILE) { + sb.append(sourceFile.getPath()); + sb.append(":"); + } + int line = loc.getLine(); + sb.append("" + line); + + int column = loc.getColumn(); + if (column != ISourceLocation.NO_COLUMN) { + sb.append(":" + column); + } + + return sb.toString(); + } + + /** + * Render message in a line. + * IMessage.Kind is always printed, then any unqualified exception class, + * then the remainder of text and location according to their relative scale, + * all to fit in max characters or less. + * This does not render thrown except for the unqualified class name + * @param max the number of characters - forced to 32..10000 + * @param textScale relative proportion to spend on message and/or exception + * message, relative to source location - if 0, message is suppressed + * @param locScale relative proportion to spend on source location + * suppressed if 0 + * @return "((IMessage) null)" or message per spec + */ + public static String renderMessageLine( + IMessage message, + int textScale, + int locScale, + int max) { + + if (null == message) { + return "((IMessage) null)"; + } + if (max < 32) { + max = 32; + } else if (max > 10000) { + max = 10000; + } + if (0 > textScale) { + textScale = -textScale; + } + if (0 > locScale) { + locScale = -locScale; + } + + String text = message.getMessage(); + Throwable thrown = message.getThrown(); + ISourceLocation sl = message.getISourceLocation(); + IMessage.Kind kind = message.getKind(); + StringBuffer result = new StringBuffer(); + result.append(kind.toString()); + result.append(": "); + if (null != thrown) { + result.append(LangUtil.unqualifiedClassName(thrown) + " "); + if ((null == text) || ("".equals(text))) { + text = thrown.getMessage(); + } + } + + if (0 == textScale) { + text = ""; + } else if ((null != text) && (null != thrown)) { + // decide between message and exception text? + String s = thrown.getMessage(); + if ((null != s) && (0 < s.length())) { + text += " - " + s; + } + } + String loc = ""; + if ((0 != locScale) && (null != sl)) { + File f = sl.getSourceFile(); + if (f == ISourceLocation.NO_FILE) { + f = null; + } + if (null != f) { + loc = f.getName(); + } + int line = sl.getLine(); + int col = sl.getColumn(); + int end = sl.getEndLine(); + if ((0 == line) && (0 == col) && (0 == end)) { + // ignore numbers if default + } else { + loc += ":" + line + (col == 0 ? "" : ":" + col); + if (line != end) { // XXX consider suppressing nonstandard... + loc += ":" + end; + } + } + if (!LangUtil.isEmpty(loc)) { + loc = "@[" + loc; // matching "]" added below after clipping + } + } + + // now budget between text and loc + float totalScale = locScale + textScale; + float remainder = max - result.length() - 4; + if ((remainder > 0) && (0 < totalScale)) { + int textSize = (int) (remainder * textScale/totalScale); + int locSize = (int) (remainder * locScale/totalScale); + // adjust for underutilization + int extra = locSize - loc.length(); + if (0 < extra) { + locSize = loc.length(); + textSize += extra; + } + extra = textSize - text.length(); + if (0 < extra) { + textSize = text.length(); + if (locSize < loc.length()) { + locSize += extra; + } + } + if (locSize > loc.length()) { + locSize = loc.length(); + } + if (textSize > text.length()) { + textSize = text.length(); + } + if (0 < textSize) { + result.append(text.substring(0, textSize)); + } + if (0 < locSize) { + if (0 < textSize) { + result.append(" "); + } + result.append(loc.substring(0, locSize) + "]"); + } + } + return result.toString(); + } + + /** @return String of the form "{(# {type}) }.." for message kinds, skipping 0 */ + public static String renderCounts(IMessageHolder holder) { + if (0 == holder.numMessages(null, false)) { + return "(0 messages)"; + } + StringBuffer sb = new StringBuffer(); + for (Iterator iter = IMessage.KINDS.iterator(); iter.hasNext();) { + IMessage.Kind kind = (IMessage.Kind) iter.next(); + int num = holder.numMessages(kind, false); + if (0 < num) { + sb.append(" (" + num + " " + kind + ") "); + } + } + return sb.toString(); + } + + /** + * Factory for handler adapted to PrintStream + * XXX weak - only handles println(String) + * @param handler the IMessageHandler sink for the messages generated + * @param kind the IMessage.Kind of message to create + * @param overage the OuputStream for text not captured by the handler + * (if null, System.out used) + * @throws IllegalArgumentException if kind or handler is null + */ + public static PrintStream handlerPrintStream(final IMessageHandler handler, + final IMessage.Kind kind, final OutputStream overage, final String prefix) { + LangUtil.throwIaxIfNull(handler, "handler"); + LangUtil.throwIaxIfNull(kind, "kind"); + class HandlerPrintStream extends PrintStream { + HandlerPrintStream() { + super(null == overage ? System.out : overage); + } + public void println() { + println(""); + } + public void println(Object o) { + println(null == o ? "null" : o.toString()); + } + public void println(String input) { + String textMessage = (null == prefix ? input : prefix + input); + IMessage m = new Message(textMessage, kind, null, null); + handler.handleMessage(m); + } + } + return new HandlerPrintStream(); + } + + /** utility class */ + private MessageUtil() {} +} diff --git a/bridge/src/org/aspectj/bridge/MessageWriter.java b/bridge/src/org/aspectj/bridge/MessageWriter.java new file mode 100644 index 000000000..467002c3b --- /dev/null +++ b/bridge/src/org/aspectj/bridge/MessageWriter.java @@ -0,0 +1,67 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + +package org.aspectj.bridge; + +import java.io.PrintWriter; + +/** + * An IMessageHandler implementation that writes all to a PrintWriter. + * Clients may set this up to throw AbortException for FAIL or ERROR messages. + * Subclasses may control whether messages are printed and how they + * are rendered by overriding render(IMessage). + */ +public class MessageWriter implements IMessageHandler { + + protected PrintWriter writer; + protected boolean abortOnFailure; + public MessageWriter(PrintWriter writer, boolean abortOnFailure) { + this.writer = (null != writer ? writer : new PrintWriter(System.out)); + this.abortOnFailure = abortOnFailure; + } + + /** + * Handle message by printing and + * (if abortOnFailure) throwing an AbortException if + * the messages is a failure or an abort (but not for errors). + * @see org.aspectj.bridge.IMessageHandler#handleMessage(IMessage) + */ + public boolean handleMessage(IMessage message) throws AbortException { + if ((null != message) && !isIgnoring(message.getKind())) { + String result = render(message); + if (null != result) { + writer.println(result); + writer.flush(); + if (abortOnFailure + && (message.isFailed() || message.isAbort())) { + throw new AbortException(message); + } + } + } + return true; + } + + /** + * @see org.aspectj.bridge.IMessageHandler#isIgnoring(Kind) + */ + public boolean isIgnoring(IMessage.Kind kind) { + // XXX share MessageHandler implementation in superclass + return false; + } + + /** @return null to not print, or message rendering (including newlines) */ + protected String render(IMessage message) { + return message.toString(); + } + +} diff --git a/bridge/src/org/aspectj/bridge/ReflectionFactory.java b/bridge/src/org/aspectj/bridge/ReflectionFactory.java new file mode 100644 index 000000000..f78352416 --- /dev/null +++ b/bridge/src/org/aspectj/bridge/ReflectionFactory.java @@ -0,0 +1,106 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + +package org.aspectj.bridge; + +import java.lang.reflect.Constructor; +import java.util.Arrays; + +/** + * + */ +public class ReflectionFactory { // XXX lease, pool + public static final String OLD_AJC = "bridge.tools.impl.OldAjc"; + public static final String ECLIPSE = "org.aspectj.ajdt.ajc.AjdtCommand"; + + private static final Object[] NONE = new Object[0]; + + /** + * Produce a compiler as an ICommand. + * @param cname the fully-qualified class name of the command + * to create by reflection (assuming a public no-argument constructor). + * @return ICommand compiler or null + */ + public static ICommand makeCommand(String cname, + IMessageHandler errorSink) { + return (ICommand) make(ICommand.class, cname, NONE, errorSink); + } + + /** + * Make an object of type c by reflectively loading the class + * cname and creating an instance using args (if any), + * signalling errors (if any) to any errorSink. + */ + private static Object make(Class c, String cname, Object[] args, + IMessageHandler errorSink) { + final boolean makeErrors = (null != errorSink); + Object result = null; + try { + final Class cfn = Class.forName(cname); + String error = null; + if (args == NONE) { + result = cfn.newInstance(); + } else { + Class[] types = getTypes(args); + Constructor constructor = cfn.getConstructor(types); + if (null != constructor) { + result = constructor.newInstance(args); + } else { + if (makeErrors) { + error = "no constructor for " + c + " using " + + Arrays.asList(types); + } + } + } + if (null != result) { + if (!c.isAssignableFrom(result.getClass())) { + if (makeErrors) { + error = "expecting type " + c + " got " + result.getClass(); + } + result = null; + } + } + if (null != error) { + IMessage mssg = new Message(error, IMessage.FAIL, null, null); + errorSink.handleMessage(mssg); + } + } catch (Throwable t) { + if (makeErrors) { + String mssg = "ReflectionFactory unable to load " + + cname + " as " + c.getName(); + IMessage m = new Message(mssg, IMessage.FAIL, t, null); + errorSink.handleMessage(m); + } + } + return result; + } + + /** + * @return Class[] with types of args or matching null elements + */ + private static Class[] getTypes(Object[] args) { + if ((null == args) || (0 < args.length)) { + return new Class[0]; + } else { + Class[] result = new Class[args.length]; + for (int i = 0; i < result.length; i++) { + if (null != args[i]) { + result[i] = args[i].getClass(); + } + } + return result; + } + } + + private ReflectionFactory(){} +} diff --git a/bridge/src/org/aspectj/bridge/SourceLocation.java b/bridge/src/org/aspectj/bridge/SourceLocation.java new file mode 100644 index 000000000..5579082b6 --- /dev/null +++ b/bridge/src/org/aspectj/bridge/SourceLocation.java @@ -0,0 +1,132 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + +package org.aspectj.bridge; + + +import org.aspectj.util.LangUtil; + +import java.io.File; + +/** + * Immutable source location. + * This guarantees that the source file is not null + * and that the numeric values are positive and line <= endLine. + * @see org.aspectj.lang.reflect.SourceLocation + * @see org.aspectj.compiler.base.parser.SourceInfo + * @see org.aspectj.tools.ide.SourceLine + * @see org.aspectj.testing.harness.ErrorLine + */ +public class SourceLocation implements ISourceLocation, java.io.Serializable { + + /** used when SourceLocation is not available */ + public static final ISourceLocation UNKNOWN + = new SourceLocation(ISourceLocation.NO_FILE, 0, 0, 0); + + /** @throws IllegalArgumentException if the input would not be a valid line */ + public static final void validLine(int line) { + if (line < 0) { + throw new IllegalArgumentException("negative line: " + line); + } else if (line > ISourceLocation.MAX_LINE) { + throw new IllegalArgumentException("line too large: " + line); + } + } + + /** @throws IllegalArgumentException if the input would not be a valid column */ + public static final void validColumn(int column) { + if (column < 0) { + throw new IllegalArgumentException("negative column: " + column); + } else if (column > ISourceLocation.MAX_COLUMN) { + throw new IllegalArgumentException("column too large: " + column); + } + } + + private final File sourceFile; + private final int line; + private final int column; + private final int endLine; + private boolean noColumn; + + /** + * Same as SourceLocation(file, line, line, 0), + * except that column is not rendered during toString() + */ + public SourceLocation(File file, int line) { + this(file, line, line, NO_COLUMN); + } + + /** same as SourceLocation(file, line, endLine, ISourceLocation.NO_COLUMN) */ + public SourceLocation(File file, int line, int endLine) { + this(file, line, endLine, ISourceLocation.NO_COLUMN); + } + + /** + * @param file File of the source; if null, use ISourceLocation.NO_FILE, not null + * @param line int starting line of the location - positive number + * @param endLine int ending line of the location - <= starting line + * @param column int character position of starting location - positive number + */ + public SourceLocation(File file, int line, int endLine, int column) { + if (column == NO_COLUMN) { + column = 0; + noColumn = true; + } + if (null == file) { + file = ISourceLocation.NO_FILE; + } + validLine(line); + validLine(endLine); + LangUtil.throwIaxIfFalse(line <= endLine , line + " > " + endLine); + LangUtil.throwIaxIfFalse(column >= 0, "negative column: " + column); + this.sourceFile = file; + this.line = line; + this.column = column; + this.endLine = endLine; + } + + public File getSourceFile() { + return sourceFile; + } + public int getLine() { + return line; + } + + /** + * @return int actual column or 0 if not available per constructor treatment + * of ISourceLocation.NO_COLUMN + */ + public int getColumn() { + return column; + } + + public int getEndLine() { + return line; + } + + /** @return String {file:}line{:column} */ + public String toString() { + StringBuffer sb = new StringBuffer(); + + if (sourceFile != ISourceLocation.NO_FILE) { + sb.append(sourceFile.getPath()); + sb.append(":"); + } + sb.append("" + line); + if (!noColumn) { + sb.append(":" + column); + } + return sb.toString(); + } + + +} diff --git a/bridge/src/org/aspectj/bridge/Version.java b/bridge/src/org/aspectj/bridge/Version.java new file mode 100644 index 000000000..bbaa2dad3 --- /dev/null +++ b/bridge/src/org/aspectj/bridge/Version.java @@ -0,0 +1,67 @@ +/* ******************************************************************* + * Copyright (c) 1999-2002 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.bridge; + +import java.text.ParsePosition; +import java.text.SimpleDateFormat; +import java.util.Date; + +/** release-specific version information */ +public class Version { + + /** default version value for development version */ + public static final String DEVELOPMENT = "DEVELOPMENT"; + + /** default time value for development version */ + public static final long NOTIME = 0L; + + /** set by build script */ + public static final String text = DEVELOPMENT; + + /** + * Time text set by build script using SIMPLE_DATE_FORMAT. + * (if DEVELOPMENT version, invalid) + */ + public static final String time_text = ""; + + /** + * time in seconds-since-... format, used by programmatic clients. + * (if DEVELOPMENT version, NOTIME) + */ + public static final long time; + + /** format used by build script to set time_text */ + public static final String SIMPLE_DATE_FORMAT = ""; + + // if not DEVELOPMENT version, read time text using format used to set time + static { + long foundTime = NOTIME; + if (!DEVELOPMENT.equals(text)) { + try { + SimpleDateFormat format = new SimpleDateFormat(SIMPLE_DATE_FORMAT); + ParsePosition pos = new ParsePosition(0); + Date date = format.parse(time_text, pos); + foundTime = date.getTime(); + } catch (Throwable t) { + } + } + time = foundTime; + } +} + + + + + diff --git a/bridge/testsrc/BridgeModuleTests.java b/bridge/testsrc/BridgeModuleTests.java new file mode 100644 index 000000000..db563fd49 --- /dev/null +++ b/bridge/testsrc/BridgeModuleTests.java @@ -0,0 +1,30 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + + +// default package + +import junit.framework.*; +import junit.framework.Test; + +public class BridgeModuleTests extends TestCase { + + public static Test suite() { + TestSuite suite = new TestSuite(BridgeModuleTests.class.getName()); + suite.addTest(org.aspectj.bridge.BridgeTests.suite()); + return suite; + } + + public BridgeModuleTests(String name) { super(name); } + +} diff --git a/bridge/testsrc/org/aspectj/bridge/BridgeTests.java b/bridge/testsrc/org/aspectj/bridge/BridgeTests.java new file mode 100644 index 000000000..2ab9b9ac1 --- /dev/null +++ b/bridge/testsrc/org/aspectj/bridge/BridgeTests.java @@ -0,0 +1,33 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.bridge; + +import junit.framework.*; + +public class BridgeTests extends TestCase { + + public static Test suite() { + TestSuite suite = new TestSuite(BridgeTests.class.getName()); + //$JUnit-BEGIN$ + suite.addTestSuite(CountingMessageHandlerTest.class); + suite.addTestSuite(MessageTest.class); + suite.addTestSuite(VersionTest.class); + //$JUnit-END$ + return suite; + } + + public BridgeTests(String name) { super(name); } + +} diff --git a/bridge/testsrc/org/aspectj/bridge/CountingMessageHandlerTest.java b/bridge/testsrc/org/aspectj/bridge/CountingMessageHandlerTest.java new file mode 100644 index 000000000..423501e13 --- /dev/null +++ b/bridge/testsrc/org/aspectj/bridge/CountingMessageHandlerTest.java @@ -0,0 +1,90 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + +package org.aspectj.bridge; + + +import java.io.File; +import java.util.Arrays; +import java.util.BitSet; +import java.util.Comparator; +import java.util.List; +import java.util.ListIterator; + +import junit.framework.AssertionFailedError; +import junit.framework.TestCase; +import junit.textui.TestRunner; + +/** + * + */ +public class CountingMessageHandlerTest extends TestCase { + + private static final String ME + = "org.aspectj.bridge.CountingMessageHandlerTest"; // XXX + + /** @param args ignored */ + public static void main(String[] args) { + TestRunner.main(new String[] {ME}); + } + + /** + * Constructor for MessageTest. + * @param name + */ + public CountingMessageHandlerTest(String name) { + super(name); + } + + public void testSimpleWrapping() { + MessageHandler m = new MessageHandler(); + CountingMessageHandler me = new CountingMessageHandler(m); + checkCountingMessageHandler(me); + } + + public void testCounterWrapping() { + MessageHandler m = new MessageHandler(); + CountingMessageHandler first = new CountingMessageHandler(m); + CountingMessageHandler me = new CountingMessageHandler(first); + checkCountingMessageHandler(me); + } + + void checkCountingMessageHandler(CountingMessageHandler me) { + MessageUtil.warn(me, "warn 1"); + assertTrue(!me.hasErrors()); + assertEquals(0 , me.numMessages(IMessage.ERROR, false)); + assertEquals(1 , me.numMessages(IMessage.WARNING, false)); + assertEquals(0 , me.numMessages(IMessage.INFO, false)); + assertEquals(0 , me.numMessages(IMessage.ERROR, true)); + assertEquals(1 , me.numMessages(IMessage.WARNING, true)); + assertEquals(1 , me.numMessages(IMessage.INFO, true)); + + MessageUtil.info(me, "info 1"); + assertTrue(!me.hasErrors()); + assertEquals(0 , me.numMessages(IMessage.ERROR, false)); + assertEquals(1 , me.numMessages(IMessage.WARNING, false)); + assertEquals(1 , me.numMessages(IMessage.INFO, false)); + assertEquals(0 , me.numMessages(IMessage.ERROR, true)); + assertEquals(1 , me.numMessages(IMessage.WARNING, true)); + assertEquals(2 , me.numMessages(IMessage.INFO, true)); + + MessageUtil.error(me, "error 1"); + assertTrue(me.hasErrors()); + assertEquals(1 , me.numMessages(IMessage.ERROR, false)); + assertEquals(1 , me.numMessages(IMessage.WARNING, false)); + assertEquals(1 , me.numMessages(IMessage.INFO, false)); + assertEquals(1 , me.numMessages(IMessage.ERROR, true)); + assertEquals(2 , me.numMessages(IMessage.WARNING, true)); + assertEquals(3 , me.numMessages(IMessage.INFO, true)); + } +} diff --git a/bridge/testsrc/org/aspectj/bridge/MessageTest.java b/bridge/testsrc/org/aspectj/bridge/MessageTest.java new file mode 100644 index 000000000..6f370eeb3 --- /dev/null +++ b/bridge/testsrc/org/aspectj/bridge/MessageTest.java @@ -0,0 +1,300 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + +package org.aspectj.bridge; + +import java.io.File; +import java.util.Arrays; +import java.util.BitSet; +import java.util.Comparator; +import java.util.List; +import java.util.ListIterator; + +import junit.framework.AssertionFailedError; +import junit.framework.TestCase; +import junit.textui.TestRunner; + +/** + * + */ +public class MessageTest extends TestCase { + + private static final String ME + = "org.aspectj.bridge.MessageTest"; // XXX + + /** @param args ignored */ + public static void main(String[] args) { + TestRunner.main(new String[] {ME}); + } + + /** + * Constructor for MessageTest. + * @param name + */ + public MessageTest(String name) { + super(name); + } + + void checkListOrder(List list, Comparator c) { // XXX util + assertNotNull(list); + assertNotNull(c); + ListIterator it = list.listIterator(); + Object last = null; + Object current = null; + while (it.hasNext()) { + current = it.next(); + if (null != last) { + int i = c.compare(last, current); + if (i > 0) { + assertTrue( last + " > " + current + " (" + i + ")", false); + } + } + last = current; + } + } + + public void testKindOrder() { + // first briefly validate the checker + checkListOrder(Arrays.asList(new String[] { "a", "b", "C" }), + String.CASE_INSENSITIVE_ORDER); + checkListOrder(IMessage.KINDS, IMessage.Kind.COMPARATOR); + } + + public void testMessageHandler() { + boolean handleMessageResult = true; + checkEmptyMessageHolder(new MessageHandler(handleMessageResult), handleMessageResult); + handleMessageResult = false; + checkEmptyMessageHolder(new MessageHandler(handleMessageResult), handleMessageResult); + } + + public void checkEmptyMessageHolder( + IMessageHolder h, + final boolean handleMessageResult) { + // { INFO, DEBUG, WARNING, ERROR, FAIL, ABORT })); + assertNotNull(h); + assertTrue(!h.hasAnyMessage(null, true)); + assertTrue(!h.hasAnyMessage(null, false)); + assertTrue(!h.hasAnyMessage(IMessage.INFO, true)); + assertTrue(!h.hasAnyMessage(IMessage.INFO, false)); + + assertTrue(handleMessageResult == h.handleMessage(make("error 1", IMessage.ERROR))); + assertTrue(handleMessageResult == h.handleMessage(make("error 2", IMessage.ERROR))); + assertTrue(handleMessageResult == h.handleMessage(make("fail 1", IMessage.FAIL))); + assertTrue(handleMessageResult == h.handleMessage(make("fail 2", IMessage.FAIL))); + assertTrue(handleMessageResult == h.handleMessage(make("debug 1", IMessage.DEBUG))); + assertTrue(handleMessageResult == h.handleMessage(make("debug 2", IMessage.DEBUG))); + + assertTrue(h.hasAnyMessage(null, true)); + assertTrue(h.hasAnyMessage(null, false)); + assertTrue(h.hasAnyMessage(IMessage.ERROR, true)); + assertTrue(h.hasAnyMessage(IMessage.ERROR, false)); + assertTrue(h.hasAnyMessage(IMessage.FAIL, true)); + assertTrue(h.hasAnyMessage(IMessage.FAIL, false)); + assertTrue(h.hasAnyMessage(IMessage.DEBUG, true)); + assertTrue(h.hasAnyMessage(IMessage.DEBUG, false)); + + assertTrue(!h.hasAnyMessage(IMessage.INFO, IMessageHolder.EQUAL)); + assertTrue(!h.hasAnyMessage(IMessage.WARNING, IMessageHolder.EQUAL)); + assertTrue(!h.hasAnyMessage(IMessage.ABORT, IMessageHolder.EQUAL)); + assertTrue(h.hasAnyMessage(IMessage.INFO, IMessageHolder.ORGREATER)); + assertTrue(h.hasAnyMessage(IMessage.WARNING, IMessageHolder.ORGREATER)); + assertTrue(!h.hasAnyMessage(IMessage.ABORT, IMessageHolder.ORGREATER)); + + assertTrue(0 == h.numMessages(IMessage.INFO, IMessageHolder.EQUAL)); + assertTrue(0 == h.numMessages(IMessage.WARNING, IMessageHolder.EQUAL)); + assertTrue(0 == h.numMessages(IMessage.ABORT, IMessageHolder.EQUAL)); + + assertTrue(6 == h.numMessages(null, IMessageHolder.ORGREATER)); + assertTrue(6 == h.numMessages(null, IMessageHolder.EQUAL)); + assertTrue(6 == h.numMessages(IMessage.INFO, IMessageHolder.ORGREATER)); + assertTrue(6 == h.numMessages(IMessage.DEBUG, IMessageHolder.ORGREATER)); + assertTrue(4 == h.numMessages(IMessage.WARNING, IMessageHolder.ORGREATER)); + assertTrue(4 == h.numMessages(IMessage.ERROR, IMessageHolder.ORGREATER)); + assertTrue(2 == h.numMessages(IMessage.FAIL, IMessageHolder.ORGREATER)); + assertTrue(0 == h.numMessages(IMessage.ABORT, IMessageHolder.ORGREATER)); + + } + + public void testMessage() { + String input = "input"; + Throwable thrown = null; + ISourceLocation sl = null; + Class exClass = null; + String descriptor = "Message"; // for make(...) + IMessage.Kind kind = IMessage.INFO; + + // -- kind variants + roundTrip(input, kind, thrown, sl, descriptor, exClass); + kind = IMessage.WARNING; + roundTrip(input, kind, thrown, sl, descriptor, exClass); + kind = IMessage.ERROR; + roundTrip(input, kind, thrown, sl, descriptor, exClass); + kind = IMessage.DEBUG; + roundTrip(input, kind, thrown, sl, descriptor, exClass); + kind = IMessage.FAIL; + roundTrip(input, kind, thrown, sl, descriptor, exClass); + + // -- throwable + kind = IMessage.FAIL; + thrown = AbortException.ABORT; + input = null; + roundTrip(input, kind, thrown, sl, descriptor, exClass); + + // -- source location + kind = IMessage.WARNING; + thrown = null; + input = "type not found"; + File f = new File("some/file.java"); // XXX unchecked + sl = new SourceLocation(f, 0, 0, 0); + roundTrip(input, kind, thrown, sl, descriptor, exClass); + sl = new SourceLocation(f, 1, 1, 0); + roundTrip(input, kind, thrown, sl, descriptor, exClass); + + // -- input error tests - null kind, null input (factory-dependent) + kind = null; + exClass = IllegalArgumentException.class; + roundTrip(input, kind, thrown, sl, descriptor, exClass); + input = null; + kind = IMessage.INFO; + roundTrip(input, kind, thrown, sl, descriptor, exClass); + } + + protected IMessage make(String message, IMessage.Kind kind) { + return new Message(message, kind, null, null); + } + + /** make a Message per descriptor and input */ + protected IMessage make(String input, IMessage.Kind kind, + Throwable thrown, ISourceLocation sourceLocation, + String descriptor) { // XXX ignored for now + return new Message(input, kind, thrown, sourceLocation); + } + + /** + * Simple round-trip on the message + */ + protected void roundTrip(String input, IMessage.Kind kind, + Throwable thrown, ISourceLocation sourceLocation, + String descriptor, Class exClass) { + try { + IMessage m = make(input, kind, thrown, sourceLocation, descriptor); + roundTripCheck(m, input, kind, thrown, sourceLocation); + } catch (AssertionFailedError x) { + throw x; + } catch (Throwable t) { + assertTrue(null != exClass); + assertTrue(exClass.isAssignableFrom(t.getClass())); + } + } + + protected void roundTripCheck(IMessage message, String input, IMessage.Kind kind, + Throwable thrown, ISourceLocation sourceLocation) { + IMessage m = message; + assertTrue("not null", null != m); + assertTrue(""+input, equals(input, m.getMessage())); + assertTrue(""+kind, kind == m.getKind()); + assertTrue(""+thrown, equals(thrown, m.getThrown())); + assertTrue(""+sourceLocation, + equals(sourceLocation, m.getISourceLocation())); + String err = new KindTest().testKindSet(message, kind); + if (null != err) { + assertTrue(err, false); + } + } + + protected static boolean equals(Object one, Object two) { + if (null == one) { + return (null == two); + } else if (null == two) { + return false; + } else { + return one.equals(two); + } + } +} + +/** test correlation between message and enclosed kind */ +class KindTest { + /** order tracked in checkKindMethods() */ + static final IMessage.Kind[] KINDS = new IMessage.Kind[] + { IMessage.ABORT, IMessage.DEBUG, IMessage.ERROR, + IMessage.INFO, IMessage.WARNING, IMessage.FAIL }; + + static final List KINDLIST = Arrays.asList(KINDS); + + /** used to clear instance BitSet */ + static final BitSet UNSET = new BitSet(KINDS.length); + + + final BitSet expected = new BitSet(KINDS.length); + IMessage.Kind kind = IMessage.INFO; + + /** @return error if failed */ + public String testKindSet(IMessage m, IMessage.Kind newKind) { + IMessage.Kind oldKind = this.kind; + String result = setKind(newKind); + if (null == result) { + result = checkKindSet(m, newKind); + } + if (null == result) { + result = checkExpectedKind(m); + } + return (null != result? result : setKind(oldKind)); + } + + /** @return error if failed */ + private String setKind(IMessage.Kind kind) { + this.kind = kind; + int index = KINDLIST.indexOf(kind); + if (-1 == index) { + return "unknown kind: " + kind; + } + expected.and(UNSET); + expected.set(index); + return null; + } + + /** @return error if failed */ + String checkExpectedKind(IMessage m) { + StringBuffer result = new StringBuffer(); + for (int i = 0; i < KINDS.length; i++) { + if (expected.get(i) != checkKindMethods(m, i)) { + String s = "expected " + expected.get(i) + + " for is{Method} for " + KINDS[i]; + result.append(s + "\n"); + } + } + return (0 < result.length() ? result.toString() : null); + } + + String checkKindSet(IMessage m, IMessage.Kind kind) { + if (kind != m.getKind()) { + return "expected kind " + kind + " got " + m.getKind(); + } + return null; + } + + /** @return true if index matches isFoo() reporting */ + boolean checkKindMethods(IMessage m, int index) { + switch (index) { + case (0) : return m.isAbort(); + case (1) : return m.isDebug(); + case (2) : return m.isError(); + case (3) : return m.isInfo(); + case (4) : return m.isWarning(); + case (5) : return m.isFailed(); + default : throw new IllegalArgumentException("index=" + index); + + } + } +} diff --git a/bridge/testsrc/org/aspectj/bridge/VersionTest.java b/bridge/testsrc/org/aspectj/bridge/VersionTest.java new file mode 100644 index 000000000..2dcc1d056 --- /dev/null +++ b/bridge/testsrc/org/aspectj/bridge/VersionTest.java @@ -0,0 +1,53 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + +package org.aspectj.bridge; + +import java.text.SimpleDateFormat; +import java.util.Date; + +import junit.framework.TestCase; +import junit.textui.TestRunner; + +/** + * + */ +public class VersionTest extends TestCase { + + private static final String ME + = "org.aspectj.bridge.VersionTest"; + + /** @param args ignored */ + public static void main(String[] args) { + TestRunner.main(new String[] {ME}); + } + + /** + * Constructor for MessageTest. + * @param name + */ + public VersionTest(String name) { + super(name); + } + + public void testVersion() { + if (Version.DEVELOPMENT.equals(Version.text)) { + assertEquals(Version.time, Version.NOTIME); + } else { + Date date = new Date(Version.time); + SimpleDateFormat format = new SimpleDateFormat(Version.SIMPLE_DATE_FORMAT); + String timeString = format.format(date); + assertEquals(Version.time_text, timeString); + } + } +} diff --git a/build/installer-resources/aspectjBanner.gif b/build/installer-resources/aspectjBanner.gif Binary files differnew file mode 100644 index 000000000..d33e0a733 --- /dev/null +++ b/build/installer-resources/aspectjBanner.gif diff --git a/build/installer-resources/contents.txt b/build/installer-resources/contents.txt new file mode 100644 index 000000000..275d2b4ff --- /dev/null +++ b/build/installer-resources/contents.txt @@ -0,0 +1,3 @@ +2000 +org/aspectj/tools/ajc/Main.class + diff --git a/build/installer-resources/properties.txt b/build/installer-resources/properties.txt new file mode 100644 index 000000000..f2ee1b595 --- /dev/null +++ b/build/installer-resources/properties.txt @@ -0,0 +1,7 @@ +installer.main.class: @installer.main.class@ +installer.basedir: aspectj@build.version.majorminor@ +build.version.long: @build.version.long@ +build.version.short: @build.version.short@ +build.date: @build.date@ +build.time: @build.time@ +copyright.allRights.from1998: @copyright.allRights.from1998@ diff --git a/build/lib/BridgeVersion.java.txt b/build/lib/BridgeVersion.java.txt new file mode 100644 index 000000000..95d41d201 --- /dev/null +++ b/build/lib/BridgeVersion.java.txt @@ -0,0 +1,66 @@ +/* ******************************************************************** + * Copyright (c) 1998-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * *******************************************************************/ + +package org.aspectj.bridge; + +import java.text.ParsePosition; +import java.text.SimpleDateFormat; +import java.util.Date; + +/** release-specific version information */ +public class Version { + + /** default version value for development version */ + public static final String DEVELOPMENT = "DEVELOPMENT"; + + /** default time value for development version */ + public static final long NOTIME = 0L; + + /** set by build script */ + public static final String text = "@build.version@"; + + /** + * Time text set by build script using SIMPLE_DATE_FORMAT. + * (if DEVELOPMENT version, invalid) + */ + public static final String time_text = "@build.time@"; + + /** + * time in seconds-since-... format, used by programmatic clients. + * (if DEVELOPMENT version, NOTIME) + */ + public static final long time; + + /** format used by build script to set time_text */ + public static final String SIMPLE_DATE_FORMAT = "@build.time.format@"; + + // if not DEVELOPMENT version, read time text using format used to set time + static { + long foundTime = NOTIME; + if (!DEVELOPMENT.equals(text)) { + try { + SimpleDateFormat format = new SimpleDateFormat(SIMPLE_DATE_FORMAT); + ParsePosition pos = new ParsePosition(0); + Date date = format.parse(time_text, pos); + foundTime = date.getTime(); + } catch (Throwable t) { + } + } + time = foundTime; + } +} + + + + + diff --git a/build/products/aspectj/install/configure-auto.html b/build/products/aspectj/install/configure-auto.html new file mode 100644 index 000000000..7abdaac6b --- /dev/null +++ b/build/products/aspectj/install/configure-auto.html @@ -0,0 +1,17 @@ +<html> + +<head> +<title>auto configure</title> +</head> + +<body> + +<p>The installer has successfully found the path to your Java home (J2SE 1.3 or greater). +This path will be used as the default Java location when generating +script files for launching the AspectJ compiler and core tools. Unless +you know that this path is wrong, we suggest that you press +<b>Next</b> to continue.</p> + +</body> + +</html> diff --git a/build/products/aspectj/install/configure-hand.html b/build/products/aspectj/install/configure-hand.html new file mode 100644 index 000000000..73b3e2185 --- /dev/null +++ b/build/products/aspectj/install/configure-hand.html @@ -0,0 +1,21 @@ +<html> + +<head> +<title>configure</title> +</head> + +<body> + +<p>The installer cannot find the path to your Java home (J2SE 1.3 or greater). +Please try to find this directory on your path now. It will probably look +something like <code>jdk1.3.1</code>. If you can't find this +directory now, you may continue with the install, but you will have +to either edit your launch scripts by hand or to set the JAVA_HOME +environment variable to point to the right location. +</p> + +<p>Press <b>Next</b> when ready to continue.</p> + +</body> + +</html> diff --git a/build/products/aspectj/install/finish.html b/build/products/aspectj/install/finish.html new file mode 100644 index 000000000..204deb550 --- /dev/null +++ b/build/products/aspectj/install/finish.html @@ -0,0 +1,28 @@ +<html> + +<head> +<title>finish</title> +</head> + +<body> + +<p>The automatic installation process is complete. +We recommend you complete the installation as follows:</p> +<ol> + <li>Add <b><code>${installer.output.aspectjrt}</code></b> + to your CLASSPATH. + This small .jar file contains classes required by any + program compiled with the ajc compiler. + </li> + <li>Modify your PATH to include + <code> <b>${installer.output.dir.bin}</b></code>. + This will make it easier to run ajc and ajbrowser. + </li> +</ol> + +<p>These steps are described in more detail in <code> +<b>${installer.output.readme}</b></code>.</p> + +</body> + +</html> diff --git a/build/products/aspectj/install/install-finish.html b/build/products/aspectj/install/install-finish.html new file mode 100644 index 000000000..14281a0bf --- /dev/null +++ b/build/products/aspectj/install/install-finish.html @@ -0,0 +1,12 @@ +<html> + +<body> + +<p> </p> + +<p>The automatic installation process is complete. Press <b>Next </b>for +some important final instructions.</p> + +</body> + +</html> diff --git a/build/products/aspectj/install/install-start.html b/build/products/aspectj/install/install-start.html new file mode 100644 index 000000000..b375c68aa --- /dev/null +++ b/build/products/aspectj/install/install-start.html @@ -0,0 +1,13 @@ +<html> + +<body> + +<p> </p> + +<p>Now installing to ${installer.output.dir}...</p> + +<p>Press <b>Cancel</b> to interrupt the installation.</p> + +</body> + +</html> diff --git a/build/products/aspectj/install/intro.html b/build/products/aspectj/install/intro.html new file mode 100644 index 000000000..58e1ee4df --- /dev/null +++ b/build/products/aspectj/install/intro.html @@ -0,0 +1,25 @@ +<html> +<body> + +<h2 align="center">Installer for AspectJ<sup><small>TM</small></sup></h2> + +<p align="center">Version ${build.version.long} built on ${build.date}</p> +<p>This installs the complete AspectJ distribution, with +the compiler, structure browser, ant tasks, +documentation, and examples. +This distribution is covered by the Common Public License (see + http://www.eclipse.org/legal/cpl-v10.html). +<p> +For IDE integrations or source code, see the project home page at +http://eclipse.org/aspectj</p> + + +<p>${copyright.allRights.from1998}</p> +<p></p> + +<p>Press <b>Next</b> to continue. At any time you may press <b>Cancel</b> +to exit the installation process.</p> + +</body> + +</html> diff --git a/build/products/aspectj/install/location.html b/build/products/aspectj/install/location.html new file mode 100644 index 000000000..0398ce4a5 --- /dev/null +++ b/build/products/aspectj/install/location.html @@ -0,0 +1,11 @@ +<html> + +<body> + +<p>Please select a directory into which to install AspectJ.</p> +<p>Press <b>Install</b> to begin the installation process + to this directory.</p> + +</body> + +</html> diff --git a/build/products/tools/dist/LICENSE-TOOLS.html b/build/products/tools/dist/LICENSE-TOOLS.html new file mode 100644 index 000000000..27bbede73 --- /dev/null +++ b/build/products/tools/dist/LICENSE-TOOLS.html @@ -0,0 +1,151 @@ +<html> + +<head> +<title>AspectJ License</title> +</head> + +<BODY BGCOLOR="white"> + +<h2 align="center"><b>AspectJ<sup>TM</sup> Compiler and Core Tools License</b></h2> + +<p>This is a binary-only release. Source code +is available from +<a href="@aspectj.home.url@">@aspectj.home.url@</a></p> + +<p>The AspectJ compiler and core tools are distributed under the +Common Public License version 1.0 available + <a href="http://eclipse.org/legal/cpl-v10.html">here</a> + and copied below. + This license has been approved by +the <a href="http://www.opensource.org">Open Source Initiative</a> as +conforming to the <a href="http://www.opensource.org/osd.html">Open +Source Definition</a>. +More information about the history and rationale behind this license +can be found at the + <a href="http://eclipse.org">eclipse web site</a>.</p> + +<p> +Those portions of this distribution in the <code>org.apache</code> +Java namespace are available under the terms of +the Apache Software License, Version 1.1 +(See <a href="http://jakarta.apache.org"> + http://jakarta.apache.org</a>). +</p> +<hr> + +<P ALIGN="CENTER"><B>Common Public License - v 1.0</B> +<P><B></B><FONT SIZE="3"></FONT> +<P><FONT SIZE="3"></FONT><FONT SIZE="2">THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS COMMON PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.</FONT> +<P><FONT SIZE="2"></FONT> +<P><FONT SIZE="2"><B>1. DEFINITIONS</B></FONT> +<P><FONT SIZE="2">"Contribution" means:</FONT> + +<UL><FONT SIZE="2">a) in the case of the initial Contributor, the initial code and documentation distributed under this Agreement, and<BR CLEAR="LEFT"> +b) in the case of each subsequent Contributor:</FONT></UL> + + +<UL><FONT SIZE="2">i) changes to the Program, and</FONT></UL> + + +<UL><FONT SIZE="2">ii) additions to the Program;</FONT></UL> + + +<UL><FONT SIZE="2">where such changes and/or additions to the Program originate from and are distributed by that particular Contributor. </FONT><FONT SIZE="2">A Contribution 'originates' from a Contributor if it was added to the Program by such Contributor itself or anyone acting on such Contributor's behalf. </FONT><FONT SIZE="2">Contributions do not include additions to the Program which: (i) are separate modules of software distributed in conjunction with the Program under their own license agreement, and (ii) are not derivative works of the Program. </FONT></UL> + +<P><FONT SIZE="2"></FONT> +<P><FONT SIZE="2">"Contributor" means any person or entity that distributes the Program.</FONT> +<P><FONT SIZE="2"></FONT><FONT SIZE="2"></FONT> +<P><FONT SIZE="2">"Licensed Patents " mean patent claims licensable by a Contributor which are necessarily infringed by the use or sale of its Contribution alone or when combined with the Program. </FONT> +<P><FONT SIZE="2"></FONT><FONT SIZE="2"></FONT> +<P><FONT SIZE="2"></FONT><FONT SIZE="2">"Program" means the Contributions distributed in accordance with this Agreement.</FONT> +<P><FONT SIZE="2"></FONT> +<P><FONT SIZE="2">"Recipient" means anyone who receives the Program under this Agreement, including all Contributors.</FONT> +<P><FONT SIZE="2"><B></B></FONT> +<P><FONT SIZE="2"><B>2. GRANT OF RIGHTS</B></FONT> + +<UL><FONT SIZE="2"></FONT><FONT SIZE="2">a) </FONT><FONT SIZE="2">Subject to the terms of this Agreement, each Contributor hereby grants</FONT><FONT SIZE="2"> Recipient a non-exclusive, worldwide, royalty-free copyright license to</FONT><FONT SIZE="2" COLOR="#FF0000"> </FONT><FONT SIZE="2">reproduce, prepare derivative works of, publicly display, publicly perform, distribute and sublicense the Contribution of such Contributor, if any, and such derivative works, in source code and object code form.</FONT></UL> + + +<UL><FONT SIZE="2"></FONT></UL> + + +<UL><FONT SIZE="2"></FONT><FONT SIZE="2">b) Subject to the terms of this Agreement, each Contributor hereby grants </FONT><FONT SIZE="2">Recipient a non-exclusive, worldwide,</FONT><FONT SIZE="2" COLOR="#008000"> </FONT><FONT SIZE="2">royalty-free patent license under Licensed Patents to make, use, sell, offer to sell, import and otherwise transfer the Contribution of such Contributor, if any, in source code and object code form. This patent license shall apply to the combination of the Contribution and the Program if, at the time the Contribution is added by the Contributor, such addition of the Contribution causes such combination to be covered by the Licensed Patents. The patent license shall not apply to any other combinations which include the Contribution. No hardware per se is licensed hereunder. </FONT></UL> + + +<UL><FONT SIZE="2"></FONT></UL> + + +<UL><FONT SIZE="2">c) Recipient understands that although each Contributor grants the licenses to its Contributions set forth herein, no assurances are provided by any Contributor that the Program does not infringe the patent or other intellectual property rights of any other entity. Each Contributor disclaims any liability to Recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow Recipient to distribute the Program, it is Recipient's responsibility to acquire that license before distributing the Program.</FONT></UL> + + +<UL><FONT SIZE="2"></FONT></UL> + + +<UL><FONT SIZE="2">d) Each Contributor represents that to its knowledge it has sufficient copyright rights in its Contribution, if any, to grant the copyright license set forth in this Agreement. </FONT></UL> + + +<UL><FONT SIZE="2"></FONT></UL> + +<P><FONT SIZE="2"><B>3. REQUIREMENTS</B></FONT> +<P><FONT SIZE="2"><B></B>A Contributor may choose to distribute the Program in object code form under its own license agreement, provided that:</FONT> + +<UL><FONT SIZE="2">a) it complies with the terms and conditions of this Agreement; and</FONT></UL> + + +<UL><FONT SIZE="2">b) its license agreement:</FONT></UL> + + +<UL><FONT SIZE="2">i) effectively disclaims</FONT><FONT SIZE="2"> on behalf of all Contributors all warranties and conditions, express and implied, including warranties or conditions of title and non-infringement, and implied warranties or conditions of merchantability and fitness for a particular purpose; </FONT></UL> + + +<UL><FONT SIZE="2">ii) effectively excludes on behalf of all Contributors all liability for damages, including direct, indirect, special, incidental and consequential damages, such as lost profits; </FONT></UL> + + +<UL><FONT SIZE="2">iii)</FONT><FONT SIZE="2"> states that any provisions which differ from this Agreement are offered by that Contributor alone and not by any other party; and</FONT></UL> + + +<UL><FONT SIZE="2">iv) states that source code for the Program is available from such Contributor, and informs licensees how to obtain it in a reasonable manner on or through a medium customarily used for software exchange.</FONT><FONT SIZE="2" COLOR="#0000FF"> </FONT><FONT SIZE="2" COLOR="#FF0000"></FONT></UL> + + +<UL><FONT SIZE="2" COLOR="#FF0000"></FONT><FONT SIZE="2"></FONT></UL> + +<P><FONT SIZE="2">When the Program is made available in source code form:</FONT> + +<UL><FONT SIZE="2">a) it must be made available under this Agreement; and </FONT></UL> + + +<UL><FONT SIZE="2">b) a copy of this Agreement must be included with each copy of the Program. </FONT></UL> + +<P><FONT SIZE="2"></FONT><FONT SIZE="2" COLOR="#0000FF"><STRIKE></STRIKE></FONT> +<P><FONT SIZE="2" COLOR="#0000FF"><STRIKE></STRIKE></FONT><FONT SIZE="2">Contributors may not remove or alter any copyright notices contained within the Program. </FONT> +<P><FONT SIZE="2"></FONT> +<P><FONT SIZE="2">Each Contributor must identify itself as the originator of its Contribution, if any, in a manner that reasonably allows subsequent Recipients to identify the originator of the Contribution. </FONT> +<P><FONT SIZE="2"></FONT> +<P><FONT SIZE="2"><B>4. COMMERCIAL DISTRIBUTION</B></FONT> +<P><FONT SIZE="2">Commercial distributors of software may accept certain responsibilities with respect to end users, business partners and the like. While this license is intended to facilitate the commercial use of the Program, the Contributor who includes the Program in a commercial product offering should do so in a manner which does not create potential liability for other Contributors. Therefore, if a Contributor includes the Program in a commercial product offering, such Contributor ("Commercial Contributor") hereby agrees to defend and indemnify every other Contributor ("Indemnified Contributor") against any losses, damages and costs (collectively "Losses") arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Contributor to the extent caused by the acts or omissions of such Commercial Contributor in connection with its distribution of the Program in a commercial product offering. The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement. In order to qualify, an Indemnified Contributor must: a) promptly notify the Commercial Contributor in writing of such claim, and b) allow the Commercial Contributor to control, and cooperate with the Commercial Contributor in, the defense and any related settlement negotiations. The Indemnified Contributor may participate in any such claim at its own expense.</FONT> +<P><FONT SIZE="2"></FONT> +<P><FONT SIZE="2">For example, a Contributor might include the Program in a commercial product offering, Product X. That Contributor is then a Commercial Contributor. If that Commercial Contributor then makes performance claims, or offers warranties related to Product X, those performance claims and warranties are such Commercial Contributor's responsibility alone. Under this section, the Commercial Contributor would have to defend claims against the other Contributors related to those performance claims and warranties, and if a court requires any other Contributor to pay any damages as a result, the Commercial Contributor must pay those damages.</FONT> +<P><FONT SIZE="2"></FONT><FONT SIZE="2" COLOR="#0000FF"></FONT> +<P><FONT SIZE="2" COLOR="#0000FF"></FONT><FONT SIZE="2"><B>5. NO WARRANTY</B></FONT> +<P><FONT SIZE="2">EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is</FONT><FONT SIZE="2"> solely responsible for determining the appropriateness of using and distributing </FONT><FONT SIZE="2">the Program</FONT><FONT SIZE="2"> and assumes all risks associated with its exercise of rights under this Agreement</FONT><FONT SIZE="2">, including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, </FONT><FONT SIZE="2">programs or equipment, and unavailability or interruption of operations</FONT><FONT SIZE="2">. </FONT><FONT SIZE="2"></FONT> +<P><FONT SIZE="2"></FONT> +<P><FONT SIZE="2"></FONT><FONT SIZE="2"><B>6. DISCLAIMER OF LIABILITY</B></FONT> +<P><FONT SIZE="2"></FONT><FONT SIZE="2">EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES </FONT><FONT SIZE="2">(INCLUDING WITHOUT LIMITATION LOST PROFITS),</FONT><FONT SIZE="2"> HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.</FONT> +<P><FONT SIZE="2"></FONT><FONT SIZE="2"></FONT> +<P><FONT SIZE="2"><B>7. GENERAL</B></FONT> +<P><FONT SIZE="2"></FONT><FONT SIZE="2">If any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.</FONT> +<P><FONT SIZE="2"></FONT> +<P><FONT SIZE="2">If Recipient institutes patent litigation against a Contributor with respect to a patent applicable to software (including a cross-claim or counterclaim in a lawsuit), then any patent licenses granted by that Contributor to such Recipient under this Agreement shall terminate as of the date such litigation is filed. In addition, if Recipient institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipient's patent(s), then such Recipient's rights granted under Section 2(b) shall terminate as of the date such litigation is filed. </FONT><FONT SIZE="2"></FONT> +<P><FONT SIZE="2"></FONT> +<P><FONT SIZE="2">All Recipient's rights under this Agreement shall terminate if it fails to comply with any of the material terms or conditions of this Agreement and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If all Recipient's rights under this Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable. However, Recipient's obligations under this Agreement and any licenses granted by Recipient relating to the Program shall continue and survive. </FONT><FONT SIZE="2"></FONT> +<P><FONT SIZE="2"></FONT> +<P><FONT SIZE="2"></FONT><FONT SIZE="2">Everyone is permitted to copy and distribute copies of this Agreement, but in order to avoid inconsistency the Agreement is copyrighted and may only be modified in the following manner. The Agreement Steward reserves the right to </FONT><FONT SIZE="2">publish new versions (including revisions) of this Agreement from time to </FONT><FONT SIZE="2">time. No one other than the Agreement Steward has the right to modify this Agreement. IBM is the initial Agreement Steward. IBM may assign the responsibility to serve as the Agreement Steward to a suitable separate entity. </FONT><FONT SIZE="2">Each new version of the Agreement will be given a distinguishing version number. The Program (including Contributions) may always be distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to distribute the Program (including its Contributions) under the new </FONT><FONT SIZE="2">version. </FONT><FONT SIZE="2">Except as expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the intellectual property of any Contributor under this Agreement, whether expressly, </FONT><FONT SIZE="2">by implication, estoppel or otherwise</FONT><FONT SIZE="2">.</FONT><FONT SIZE="2"> All rights in the Program not expressly granted under this Agreement are reserved.</FONT> +<P><FONT SIZE="2"></FONT> +<P><FONT SIZE="2">This Agreement is governed by the laws of the State of New York and the intellectual property laws of the United States of America. No party to this Agreement will bring a legal action under this Agreement more than one year after the cause of action arose. Each party waives its rights to a jury trial in any resulting litigation.</FONT> +<P><FONT SIZE="2"></FONT><FONT SIZE="2"></FONT> +<P><FONT SIZE="2"></FONT> + +<hr> +</body> + +</html> diff --git a/build/products/tools/dist/README-11.html b/build/products/tools/dist/README-11.html new file mode 100644 index 000000000..b58a650da --- /dev/null +++ b/build/products/tools/dist/README-11.html @@ -0,0 +1,1067 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<html> <head> +<title>AspectJ 1.1beta1 -- Readme</title> +<style type="text/css"> +<!-- + P { margin-left: 20px; } + PRE { margin-left: 20px; } + LI { margin-left: 20px; } + H4 { margin-left: 20px; } + H3 { margin-left: 10px; } +--> +</style> +</head> + +<body> +<p align="right"><small> +© Copyright 2002 Palo Alto Research Center, Incorporated. +All rights reserved. +</small></p> + +<h1>AspectJ Readme</h1> + +<p align="right"><i>Version 1.1beta1</i></p> + +<p> This is an early-access release of AspectJ 1.1. It includes a small +number of new language features as well as major improvements to the +functionality of the tools. </p> + +<p> This document is intended to completely describe the 1.1 language +and tools in relation to AspectJ 1.0.6. With that in mind, many +features are documented sketchily with the flag "not working in +1.1beta1", but we believe it to include some description of all the +features of 1.1 that are different from those in 1.0.6. +</p> + +<p>The full documentation package for the 1.1 release is not yet +ready, so this document goes into a bit more detail than usual changes +documents. All descriptions here should be read as changes to the +<a href="http://aspectj.org/documentation/dist">The AspectJ 1.0.6 +documentation</a>. </p> + +<p> The changes from the 1.1alpha release are: +</p> + + +<ul> + <li>More 1.0 features have been implemented: <code>perthis</code>, + <code>pertarget</code>, <code>percflow</code>, + <code>withincode</code>, <code>if</code>, <code>cflow</code>, + <code>cflowbelow</code>, <code>privileged aspects</code>, and + <code>declare soft</code>. </li> + + <li>Two new compiler flags, <a href="#NO_WEAVE">-noweave</a> and + <a href="#BINARY_ASPECTS">-aspectpath</a>, are supported.</li> + + <li>The <a href="#XLINT">-Xlint</a> option is now partially supported.</li> + + <li>Source information is better maintained.</li> + + <li><code>declare soft</code> and inter-type field declarations have + additional <a href="#NEW_LIMITATIONS">restrictions</a> that were not + present in 1.0. </li> + + <li>The status of <a href="#tools">ajdoc</a> is under consideration.</li> + + <li>Eclipse 2.0 will be supported by an upcoming AJDT release</li> + + <li>Netbeans 3.4 is supported, and compiler message output is better + integrated.</li> + + <li>Method executions now show up in the tools' structure model.</li> +</ul> + +<p> This document begins with short descriptions of changes from the +1.0 release. +</p> + +<ul> + <li><a href="#language">the language</a>,</li> + <li><a href="#compiler">the compiler</a>,</li> + <li><a href="#tools">the support tools</a>,</li> + <li><a href="#runtime">the runtime</a>,</li> + <li><a href="#devenv">the development environment support</a>, and</li> + <li><a href="#sources">the sources</a>,</li> +</ul> + +<p> followed by <a href="#details">details</a> of many of the changes. +</p> + +<!-- ============================== --> +<hr> +<h2><a name="language">The Language</a></h2> + + <p> AspectJ 1.1 is a slightly different language than AspectJ 1.0. + In all but a few cases, programs written in AspectJ 1.0 should + compile correctly in AspectJ 1.1. In many cases, there are + new or preferred forms in AspectJ 1.1. However, some AspectJ 1.0 + features are not yet implemented in 1.1, so many 1.0 programs + will not compile in this release. + </p> + + <p> + Most changes to the language are additions to expressibility + requested by our users: + </p> + + <ul> + <li><a href="#THROWS_PATTERN">Matching based on throws</a>: You can + now make finer discriminations between methods based on declared + exceptions. </li> + + <li><a href="#NEW_PCDS">New kinded pointcut designators</a>: Now + every kind of pointcut has a corresponding kinded pointcut + designator. </li> + + <li><a href="#PER_TYPE">New pertype aspect specifier</a>: This + is a potential feature whose status is still uncertain. </li> + </ul> + + <p> Some are have different behavior in edge cases but offer + improved power and clarity: </p> + + <ul> + <li><a href="#ASPECT_PRECEDENCE">New aspect precedence form</a>: + AspectJ 1.1 has a new declare form, declare dominates, that is + intended to replace the "dominates" clause on aspects. </li> + </ul> + + <p> But in order to support weaving into bytecode effectively, + several incompatible changes had to be made to the language: </p> + + <ul> + <li><a href="#CALLEE_SIDE_CALLS">Different callee-side call join + points</a></li> + + <li><a href="#SINGLE_INTERCLASS_TARGET">One target for intertype + declarations</a></li> + + <li><a href="#UNAVAILABLE_JOIN_POINTS">No initializer execution join + points</a></li> + + <li><a href="#CONSTRUCTOR_EXECUTION_IS_BIGGER">Initializers run + inside constructor execution join points</a></li> + + <li><a href="#WITHIN_MEMBER_TYPES">Small limitations of the within + pointcut</a></li> + + <li><a href="#WITHIN_CODE">Small limitations of the withincode + pointcut</a></li> + + <li><a href="#INSTANCEOF_ON_WILD">Can't do instanceof matching on + type patterns with wildcards</a></li> + + <li><a href="#NO_SOURCE_COLUMN">SourceLocation.getColumn() is + deprecated and will always return 0</a></li> + + </ul> + + <p><a name="NEW_LIMITATIONS">There</a> are a couple of language + limitations for things that are rarely used that make the + implementation simpler. If no one speaks up in defense of these, + they will probably become official simplifications of the language. + If defenders speak up, then they will be listed as limitations of + the current compiler implementation. </p> + + <ul> + <li>'declare soft: TYPE: POINTCUT;' In 1.0 a TYPE_PATTERN was + allowed rather than a TYPE. This limitation makes declare soft + much easier to implement efficiently.</li> + + <li>Inter-type field declarations only allow a single field per + line, i.e. this is now illegal 'int C.field1, D.field2;' This must + instead be, 'int C.field1; int D.field2;'</li> + </ul> + + <p>Finally, there are several AspectJ-1.0 language features that are + unimplemented in 1.1beta1 because we didn't have the time. These + features should all be available in 1.1rc1.</p> + + <ul> + <li><a href="#MISSING_LANGUAGE">Language features unimplemented in + 1.1beta1</a>: initialization, preinitialization and handler + pcds/join points; args pcd with multiple ..'s; inter-type + constructor declarations. </li> + </ul> + +<!-- ============================== --> +<hr> +<h2><a name="compiler">The Compiler</a></h2> + + <p> The compiler for AspectJ 1.1 is different than the compiler for + AspectJ 1.0. While this document describes the differences in the + compiler, it's worthwhile noting that much effort has been made to + make sure that the interface to ajc 1.1 is, as much as possible, the + same as the interface to ajc 1.0. There are two important changes + under the hood, however. </p> + + <p> First, The 1.1 compiler is implemented on top of IBM's + open-source Eclipse compiler. This has two benefits: It allows us + to concentrate on the AspectJ extensions to Java and let the Eclipse + team worry about making sure the Java edge cases work, and it allows + us to piggyback on Eclipse's already mature incremental compilation + facilities. </p> + + <p> Second, ajc now cleanly deliniates compilation of source code + from assembly (or "weaving") of bytecode. The compiler still + accepts source code, but internally it transforms it into bytecode + format before weaving. </p> + + <p> This new architecture, and other changes to the compiler, allows + us to implement some features that were defined in the AspectJ 1.0 + language but not implementable in the 1.1 compiler (after returning + advice on handler join points will probably be one of those + features, when handler join points are finished). It also makes + some new features available: </p> + + <ul> + <li><a href="#SOURCEROOT">The -sourceroots option</a>: This option + takes one or more directories, and indicates that all the source + files in those directories should be passed to the compiler. </li> + + <li><a href="#BYTECODE_WEAVING">The -injars option</a>: This option + takes one or more jar files, and indicates that all the classfiles + in the jar files should be woven into. </li> + + <li><a href="#NO_WEAVE">-noweave, a compiler option to suppress + weaving</a></li> + + <li><a href="#BINARY_ASPECTS">-aspectpath, working with aspects in + .class/.jar form</a></li> + + <li><a href="#OUTJAR">The -outjar option</a>: This option indicates + that the result classfiles of compiling and weaving should be placed + in the specified jar file. </li> + + <li><a href="#XLINT">The -Xlint option is partially implemented in + 1.1beta1.</a></li> + + <li><a href="#INCREMENTAL">Incremental compilation</a>: The AspectJ + 1.1 compiler supports incremental compilation. </li> + </ul> + + <p> Some other features we plan to support for 1.1, but did not make + it into this beta release: </p> + + <ul> + <li><a href="#NO_AROUND_OPTIMIZATION">Inlining around advice</a>: The + implementation of around advice is not optimized at all in the + beta, and so will result in slower generated code than that + generated by ajc 1.0. </a></li> + + <li><a href="#NO_CALLEE_SIDE_CALL">Callee-side call join points</a>: + The beta compiler does not expose call join points unless it is + given the calling code. </li> + + <li><a href="#EXCEPTION_CHECKING">Exception checking is not + handled completely during weaving</a></li> + + <li><a href="#ERROR_MESSAGES">Error messages will sometimes be scary</a></li> + + <li><a href="#OTHER_X_OPTIONS">Various -X options</a></li> + + <li><a href="#MISSING_LANGUAGE">Several language features are unimplemented in + 1.1beta1</a></li> + </ul> + + <p> But some features of the 1.0 compiler are not supported in the + 1.1 compiler: </p> + + <ul> + <li><a href="#NO_SOURCE">Source-specific options</a>: The -source, + -usejavac, -nocomment and -workingdir options are no longer + supported</li> + + <li><a href="#NO_STRICT_LENIENT">The -strict and -lenient options</a> + </li> + + <li><a href="#NO_PORTING">The -porting option</a></li> + + <li><a href="#13_REQUIRED">J2SE 1.2 is not supported; + J2SE 1.3 or later is required;.</a></li> + </ul> + + <p> A short description of the options ajc accepts is available with + "ajc -help". </p> + <p> </p> + +<!-- ============================== --> +<hr> +<h2><a name="tools">Support Tools</a></h2> + + + <p>This release includes two new experimental Ant variants, + a CompilerAdapter to support running <tt>ajc</tt> with the Javac task + by setting the build.compiler property and a task that supports + incremental mode by watching a file. + </p> + + <p>This release does not include <tt>ajdoc</tt>, the documentation tool for + AspectJ sources, and its 1.1 release will trail the others' by weeks. + <tt>Ajdoc</tt> is deeply dependent on the abstract syntax tree + classes from the old compiler, so it needs a bottom-up rewrite. + We think it best to use this opportunity to implement more general + API's for publishing and rendering static structure. Because those API's + are last to settle in the new architecture, and because the compiler itself + is a higher priority, we are delaying work on <tt>ajdoc</tt> until + after the 1.1 release.</p> + + <p>AspectJ 1.1 will not include <tt>ajdb</tt>, the AspectJ stand-alone debugger. + It is no longer necessary because more third-party debuggers are starting + to work according to JSR 45, "Debugging support for other languages", + which we plan to support in AspectJ 1.1. </p> + +<!-- ============================== --> +<hr> +<h2><a name="runtime">The Runtime Library</a></h2> + + <p>This release has minor additions to the runtime library classes. + As with any release, you should compile and run with the runtime + library that came with your compiler, and you may run with + a later version of the library without recompiling your code.</p> + + <p> In one instance, however, they behave differently this release. + Because the AspectJ 1.1 compiler does its weaving through + bytecode, column numbers of source locations are not available. + Therefore, <code>thisJoinPoint.getSourceLocation().getColumn()</code> + is deprecated and will always return 0. </p> + +<!-- ============================== --> +<hr> +<h2><a name="devenv">The AJDE Tools</a></h2> + + <p> AJDE for JBuilder, AJDE for Netbeans, and AJDE for Emacs and the + AJBrowser have not changed much. They use the batch-build mode of + the new compiler. The beta compiler does not yet produce + crosscutting structure (for anything but method executions) so there are no inline + annotations in JBuilder or Emacs support and you only see a + declaration tree in the structure views. Incremental building and + bytecode weaving are not available in the AJDE tools in the beta + release, but will be in a future release.</p> + + <p> Of note for the beta is that NetBeans 3.4 is supported, and there is now + better integration for the compiler messages output window.</p> + +<!-- ============================== --> +<hr> +<h2><a name="sources">The Sources and the Licences</a></h2> + + <p> The AspectJ tools sources are available under the + <a href="http://eclipse.org/legal/cpl-v10.html">Common Public + Licence</a> in the CVS repository + at <a href="http://eclipse.org">http://eclipse.org</a>. + </p> + + <p> More information about the sources can be found in their + respective README files. </p> + +<!-- ============================== --> +<hr> +<hr> +<h2><a name="details">Details</a></h2> + + <h3><a name="THROWS_PATTERN">Matching based on throws</a></h3> + + <p> Type patterns may now be used to pick out methods and + constructors based on their throws clauses. This allows the + following two kinds of extremely wildcarded pointcuts: </p> + + <pre> pointcut throwsMathlike(): + // each call to a method with a throws clause containing at least + // one exception exception with "Math" in its name. + call(* *(..) throws *..*Math*); + + pointcut doesNotThrowMathlike(): + // each call to a method with a throws clause containing no + // exceptions with "Math" in its name. + call(* *(..) throws !*..*Math*); + </pre> + + <p> The longwinded rules are that a method or constructor pattern + can have a "throws clause pattern". Throws clause patterns look + like: </p> + + <pre> ThrowsClausePattern: + ThrowsClausePatternItem ("," ThrowsClausePatternItem)* + + ThrowsClausePatternItem: + ["!"] TypeNamePattern + </pre> + + <p> A ThrowsClausePattern matches the ThrowsClause of any code + member signature. To match, each ThrowsClausePatternItem must + match the throws clause of the member in question. If any item + doesn't match, then the whole pattern doesn't match. This rule is + unchanged from AspectJ 1.0. </p> + + <p> If a ThrowsClausePatternItem begins with "!", then it matches + a particular throws clause if and only if <em>none</em> of the + types named in the throws clause is matched by the + TypeNamePattern. </p> + + <p> If a ThrowsClausePatternItem does not begin with "!", then it + matches a throws clause if and only if <em>any</em> of the types + named in the throws clause is matched by the TypeNamePattern.</p> + + <p> These rules are completely backwards compatible with + AspectJ 1.0. The rule for "!" matching has one potentially + surprising property, in that the two PCDs shown below will have + different matching rules. </p> + + <pre> [1] call(* *(..) throws !IOException) + [2] call(* *(..) throws (!IOException)) + + void m() throws RuntimeException, IOException {} + </pre> + + <p> [1] will NOT match the method m(), because method m's throws + clause declares that it throws IOException. [2] WILL match the + method m(), because method m's throws clause declares the it + throws some exception which does not match IOException, + i.e. RuntimeException. </p> + + <h3><a name="NEW_PCDS">New kinded pointcut designators</a></h3> + + <p> AspectJ 1.0 does not provide kinded pointcut designators for + two (rarely used) join points: preinitialization (the code that + runs before a super constructor call is made) and advice + execution. AspectJ 1.1 does not change the meaning of the join + points, but will provide two new pointcut designators to pick out + these join points, thus making join points and pointcut + designators more parallel. </p> + + <p> <code>adviceexectuion()</code> will pick out advice execution + join points. You will usually want to use <code>adviceexecution() + && within(Aspect)</code> to restrict it to only those pieces of + advice defined in a particular aspect. preinitialization join + points are not yet available in AspectJ 1.1beta1. </p> + + <h3><a name="PER_TYPE">New pertype aspect specifier</a></h3> + + <p>We're strongly considering adding a pertype aspect kind to 1.1. + This is somewhat motivated by the new <a + href="#SINGLE_INTERCLASS_TARGET">restrictions on inter-type + declarations<a>. This is also motivated by many previous request + to support a common logging idiom. Here's what pertype would look + like:</p> + + <pre> /** One instance of this aspect will be created for each class, + * interface or aspect in the com.bigboxco packages. + */ + aspect Logger pertype(com.bigboxco..*) { + /* This field holds a logger for the class. */ + Log log; + + /* This advice will run for every public execution defined by + * a type for which a Logger aspect has been created, i.e. + * any type in com.bigboxco..* + */ + before(): execution(public * *(..)) { + log.enterMethod(thisJoinPoint.getSignature().getName()); + } + + /* We can use a special constructor to initialize the log field */ + public Logger(Class myType) { + this.log = new Log(myType); + } + } + + /** External code could use aspectOf to get at the log, i.e. */ + Log l = Logger.aspectOf(com.bigboxco.Foo.class).log; + </pre> + + <p>The one open question that I see is how this should interact + with inner types. If a pertype aspect is created for an outer + type should advice in that aspect run for join points in inner + types? That is the behavior of the most common uses of this idiom. + </p> + + <h3><a name="CALLEE_SIDE_CALLS">Different callee-side call join + points</a></h3> + + <p> Much effort was made in AspectJ 1.0 to be able to advise as + many call join points as possible. When the code for the call was + passed to the compiler, ajc would allow call pointcuts could pick + out the corresponding call join point. When only the code for the + called method or constructor was passed to the compiler, however, + ajc would allow only certain call pointcuts to pick out the + corresponding join point, using an implementation technique called + "callee-side call join points". </p> + + <p> Because this was dependent on an implementation technique, it + was highlighted in the implementation limitations section of the + AspectJ documentation. The AspectJ 1.1 must make different + implementation decisions, and so will pick out different + callee-side call join points. </p> + + <p> For this release, however, the question is moot: + <a href="#NO_CALLEE_SIDE_CALL">No handling</a> of callee-side call + join points are implemented for AspectJ 1.1beta1. </p> + + <h3><a name="SINGLE_INTERCLASS_TARGET">One target for intertype + declarations</a></h3> + + <p> Intertype declarations (once called "introductions") in + AspectJ 1.1 can only have one target type. So the following code + intended to declare that there is a void doStuff() method on all + subtypes of Target is not legal AspectJ 1.1 code. + </p> + + <pre> aspect A { + public void Target+.doStuff() { ... } + } + </pre> + + <p> The functionality of "multi-intertype declarations" can be + recovered by using a helper interface. + </p> + + <pre> aspect A { + private interface MyTarget {} + declare parents: Target+ implements MyTarget; + public void MyTarget.doStuff() { ... } + } + </pre> + + <p> We believe this is better style in AspectJ 1.0 as well, as it + makes clear the static type of "this" inside the method body. + </p> + + <p> The one piece of functionality that can not be easily + recovered is the ability to add static fields to many classes. We + believe that the <a href="#PER_TYPE">pertype proposal</a> provides + this functionality in a much more usable form.</p> + + <h3><a name="UNAVAILABLE_JOIN_POINTS">No initializer execution join + points</a></h3> + + <p> AspectJ 1.1 does not consider initializer execution as a + principled join point. The collection of initializer code (the + code that sets fields with initializers and the code in non-static + initializer blocks) is something that makes sense only in Java + source code, not in Java bytecode. </p> + + <h3><a name="CONSTRUCTOR_EXECUTION_IS_BIGGER">Initializers run + inside constructor execution join points</a></h3> + + <p> The code generated by the initializers in Java source code now + runs inside of constructor execution join points. This changes + how before advice runs on constructor execution join points. + Consider: </p> + + <pre> class C { + C() { } + String id = "identifier"; // this assignment + // has to happen sometime + } + aspect A { + before(C c) this(c) && execution(C.new()) { + System.out.println(c.id.length()); + } + } + </pre> + + <p> In AspectJ 1.0, this will print "10", since id is assigned its + initial value prior to the before advice's execution. However, in + AspectJ 1.1, this will throw a NullPointerExcception, since "id" + does not have a value prior to the before advice's execution. + </p> + + <p> Note that the various flavors of after returning advice are + unchanged in this respect in AspectJ 1.1. Also note that this + only matters for the execution of constructors that call a + super-constructor. Execution of constructors that call a + this-constructor are the same in AspectJ 1.1 as in AspectJ 1.0. + </p> + + <p> We believe this difference should be minimal to real programs, + since programmers using before advice on constructor execution + must always assume incomplete object initialization, since the + constructor has not yet run. </p> + + <h3><a name="WITHIN_MEMBER_TYPES">Small limitations of the within + pointcut</a></h3> + + <p> Becuase of the guarantees made (and not made) by the Java + classfile format, there are cases where AspectJ 1.1 cannot + guarantee that the within pointcut designator will pick out all + code that was originally within the source code of a certain + type. + </p> + + <p> The non-guarantee applies to code inside of anonymous and + local types inside member types. While the within pointcut + designator behaves exactly as it did in AspectJ 1.0 when given a + package-level type (like C, below), if given a member-type (like + C.InsideC, below), it is not guaranteed to capture code in + contained local and anonymous types. For example: </p> + + <pre> class C { + Thread t; + class InsideC { + void setupOuterThread() { + t = new Thread( + new Runnable() { + public void run() { + // join points with code here + // might not be captured by + // within(C.InsideC), but are + // captured by within(C) + System.out.println("hi"); + } + }); + } + } + } + </pre> + + <p> We believe the non-guarantee is small, and we haven't verified + that it is a problem in practice. </p> + + <h3><a name="WITHIN_CODE">Small limitations of the withincode + pointcut</a></h3> + + <p>The withincode pointcut has similar issues to those described + above for within. We still need to do some hard thinking about + how anonymous and local classes can be handled by this pointcut. + <b>beta</b>: This pointcut is unimplementd in the beta1 release. + </p> + + <h3><a name="INSTANCEOF_ON_WILD">Can't do instanceof matching on + type patterns with wildcard</a></h3> + + <p>The pointcut designators this, target and args specify a + dynamic test on their argument. These tests can not be performed + on type patterns with wildcards in them. The following code that + compiled under 1.0 will be an error in AspectJ-1.1:</p> + + <pre> pointcut oneOfMine(): this(com.bigboxco..*); + </pre> + + <p>The only way to implement this kind of matching in a modular + way would be to use the reflection API at runtime on the Class of + the object. This would have a very high performance cost and + possible security issues. There are two good work-arounds. If + you control the source or bytecode to the type you want to match + then you can use declare parents, i.e.:</p> + + <pre> private interface OneOfMine {} + declare parents: com.bigboxco..* implements OneOfMine; + pointcut oneOfMine(): this(OneOfMine); + </pre> + + <p>If you want the more dynamic matching and are willing to pay + for the performance, then you should use the Java reflection API + combinded with if. That would look something like:</p> + + <pre> pointcut oneOfMine(): this(Object) && + if(classMatches("com.bigboxco..*", + thisJoinPoint.getTarget().getClass())); + + static boolean classMatches(String pattern, Class _class) { + if (patternMatches(pattern, _class.getName())) return true; + ... + } + </pre> + + <p>Note: wildcard type matching still works in all other pcds that + match based on static types. So, you can use + 'within(com.bigboxco..*+)' to match any code lexically within one + of your classes or a subtype thereof. This is often a good + choice.</p> + </p> + + + <h3><a name="NO_SOURCE_COLUMN">SourceLocation.getColumn()</a></h3> + + <p>The Java .class file format contains information about the + source file and line numbers of its contents; however, it has not + information about source columns. As a result, we can not + effectively support the accesss of column information in the + reflection API. So, any calls to + thisJoinPoint.getSourceLocation().getColumn() will be marked as + deprecated by the compiler, and will always return 0.</p> + + <h3><a name="ASPECT_PRECEDENCE">Aspect precedence</a></h3> + + <p> AspectJ 1.1 has a new declare form: + </p> + + <pre> declare dominates ":" TypePatternList ";" + </pre> + + <p> This is used to declare advice ordering constraints on join + points. For example, the constraints that (1) aspects that have + Security as part of their name should dominate all other aspects, and + (2) the Logging aspect (and any aspect that extends it) should + dominate all non-security aspects, can be expressed by: </p> + + <pre> declare dominates: *..*Security*, Logging+, *; + </pre> + + <p> In the TypePatternList, the wildcard * means "any type not matched + by another type in the declare dominates". </p> + + <h4>Various cycles</h4> + + <p> It is an error for any aspect to be matched by more than one + TypePattern in a single decare dominates, so: </p> + + <pre> declare dominates: A, B, A ; // error + </pre> + + <p> However, multiple declare dominates forms may legally have this + kind of circularity. For example, each of these declare dominates is + perfectly legal: + </p> + + <pre> declare dominates: B, A; + declare dominates: A, B; + </pre> + + <p> And a system in which both constraints are active may also be + legal, so long as advice from A and B don't share a join point. So + this is an idiom that can be used to enforce that A and B are strongly + independent. </p> + + <h4>Applies to concrete aspects</h4> + + <p> Consider the following library aspects: + </p> + + <pre> abstract aspect Logging { + abstract pointcut logged(); + + before(): logged() { + System.err.println("thisJoinPoint: " + thisJoinPoint); + } + } + + aspect aspect MyProfiling { + abstract pointcut profiled(); + + Object around(): profiled() { + long beforeTime = System.currentTimeMillis(); + try { + return proceed(); + } finally { + long afterTime = System.currentTimeMillis(); + addToProfile(thisJoinPointStaticPart, + afterTime - beforeTime); + } + } + abstract void addToProfile( + org.aspectj.JoinPoint.StaticPart jp, + long elapsed); + } + </pre> + + <p> In order to use either aspect, they must be extended with + concrete aspects, say, MyLogging and MyProfiling. In AspectJ + 1.0, it was not possible to express that Logging's advice (when + concerned with the concrete aspect MyLogging) dominated + Profiling's advice (when concerned with the concrete aspect + MyProfiling) without adding a dominates clause to Logging + itself. In AspectJ 1.1, we can express that constraint with a + simple: </p> + + <pre> declare dominates: MyLogging, MyProfiling; + </pre> + + <h4>Changing order of advice for sub-aspects</h4> + + <p> By default, advice in a sub-aspect has more precedence than + advice in a super-aspect. One use of the AspectJ 1.0 dominates + form was to change this precedence: + </p> + + <pre> abstract aspect SuperA dominates SubA { + pointcut foo(): ... ; + + before(): foo() { + // in AspectJ 1.0, runs before the advice in SubA + // because of the dominates clause + } + } + + aspect SubA extends SuperA { + before(): foo() { + // in AspectJ 1.0, runs after the advice in SuperA + // because of the dominates clause + } + } + </pre> + + <p> This no longer works in AspectJ 1.1, since declare dominates only + matters for concrete aspects. Thus, if you want to regain this kind + of precedence change, you will need to refactor your aspects. + </p> + + <h3><a name="MISSING_LANGUAGE">Language features unimplemented in + 1.1beta1</a></h3> + + <p>All of these language features should be implemented in + 1.1beta1.</p> + + <ul> + <li>per's: perthis, pertarget, percflow</li> + <li>pointcut designators: withincode, if, cflow, and cflowbelow</li> + <li>joinpoints and corresponding pcds: + initialization, preinitialization and handler</li> + <li>args pcd with multiple ..'s</li> + <li>privileged aspects</li> + <li>declare soft</li> + <li>inter-type constructor declarations</li> + </ul> + + <h3><a name="SOURCEROOT">The -sourceroots option</a></h3> + + <p> The AspectJ 1.1 compiler now accepts a -sourceroots option used to + pass all .java files in particular directories to the compiler. It + takes either a single directory name, or a list of directory names + separated with the CLASSPATH separator character (":" for various + Unices, ";" for various Windows). </p> + + <p> So, if you have your project separated into a gui module and a + base module, each of which is stored in a directory tree, you might + use one of + </p> + + <pre> ajc -sourceroots /myProject/gui:/myProject/base + ajc -sourceroots d:\myProject\gui;d:\myProject\base + </pre> + + <p> This option may be used in conjunction with lst files, listing + .java files on the command line, and the -injars option. + </p> + + <p> <strong>ALPHA:</strong> In the beta release of the compiler, + only one directory can be passed to the -sourceroots option. + </p> + + <h3><a name="BYTECODE_WEAVING">The -injars option</a></h3> + + <p> The AspectJ 1.1 compiler now accepts an -injars option used to + pass all .class files in a particular jar file to the compiler. It + takes either a single directory name, or a list of directory names + separated with the CLASSPATH separator character (":" for various + Unices, ";" for various Windows). </p> + + <p> So, if MyTracing.java defines a trace aspect that you want to + apply to all the classes in myBase.jar and myGui.jar, you would use + one of: </p> + + <pre> ajc -injars /bin/myBase.jar:/bin/myGui.jar MyTracing.java + ajc -injars d:\bin\myBase.jar;d:\bin\myGui.jar MyTracing.java + </pre> + + <p> The class files in the input jars must not have had advice woven + into them, since AspectJ enforces the requirement that advice is woven + into a particular classfile only once. So if the classfiles in the + jar file are to be created with the ajc compiler (as opposed to a pure + Java compiler), they should not be compiled with any non-abstract + aspects. </p> + + <p> This option may be used in conjunction with lst files, listing + .java files on the command line, and the -sourceroots option. + </p> + + <h3><a name="OUTJAR">The -outjar option</a></h3> + + <p> The -outjar option takes the name of a jar file into which the + results of the compilation should be put. For example: + + <pre> ajc -injars myBase.jar MyTracing.java -outjar myTracedBase.jar + </pre> + + <p> No meta information is placed in the output jar file </p> + + <h3><a name="INCREMENTAL">Incremental compilation</a></h3> + + <p> The AspectJ 1.1 compiler now supports incremental compilation. + For the final release, this will work from the various IDE plugins we + ship, but for the Alpha release, it is only supported on the + command-line compiler. + </p> + + <p> When ajc is called with the -incremental option, it must also be + passed a -sourceroots option specifying a directory to incrementally + compile. Once the initial compile is done, ajc waits for console + input. Every time it reads a new line, (i.e., every time the user + hits return) ajc recompiles those input files that need recompiling. + </p> + + <h4>Limitations for 1.1beta1</h4> + + <p> some changes to classes should force re-weaving, but are not + noticed</p> + + <p> inter-type declarations, declare parents are not tracked + correctly</p> + + + <h3><a name="NO_WEAVE">-noweave, a compiler option to suppress + weaving</a></h3> + + <p> The -noweave option suppresses weaving, and generates + classfiles and that can be passed to ajc again (through the + -injars option) to generate final, woven classfiles. </p> + + <h3><a name="BINARY_ASPECTS">-aspectpath, working with aspects in .class/.jar + form</a> </h3> + + <p> When aspects are compiled into classfiles, they include all + information necessary for the ajc compiler to weave their advice + and deal with their inter-type declarations. In order for these + aspects to have an effect on a compilation process, they must be + passed to the compiler on the -aspectpath. Every .jar file on + this path will be searched for aspects and any aspects that are + found will be enabled during the compilation. The binary forms of + this aspects will be untouched. </p> + + <h3><a name="NO_AROUND_OPTIMIZATION">Inlining around advice</a></h3> + + <p> In the beta release of the compiler, around advice was + implemented in the safest way for all uses of around advice. However, + many (if not most) case of around advice do not involve, for example, + inner classes capturing proceed, and can (and will) be implemented in + more efficient styles. </p> + + <h3><a name="NO_CALLEE_SIDE_CALL">Callee-side call join + points</a></h3> + + <p> The 1.0 implementation of AspectJ, when given: + </p> + + <pre> class MyRunnable implements Runnable { + public void run() { ... } + } + + aspect A { + call(): (void run()) && target(MyRunnable) { + // do something here + } + } + </pre> + + <p> would cause A's advice to execute even when, say, java.lang.Thread + called run() on a MyRunnable instance. The beta release of AspectJ + 1.1, however, does not expose any call join point unless it is given + the calling code. And the final release of AspectJ 1.1 may have + <a href="#CALLEE_SIDE_CALLS">different callee-side call behavior</a> + than the 1.0.6 release. </p> + + <h3><a name="OTHER_X_OPTIONS">Various -X options</a></h3> + + <p> The AspectJ 1.0 compiler supported a number of options that + started with X, for "experimantal". Some of them will not be + supported in 1.1, either because the "experiment" succeeded (in + which case it's part of the normal functionality) or failed. + Others will be supported as is (or nearly so) in 1.1: + </p> + + <ul> + <li>-XOcodeSize: Not supported in 1.1beta1, since we're not + doing <em>any</em> <a href="#NO_AROUND_OPTIMIZATION">inlining of + around advice</a> in beta. This may or may not be supported in + 1.1 final: bytecode weaving gives us more leeway in inlining + anyway, so this may be the default. </li> + + <li>-XtargetNearSource: Not suppoted in 1.1beta1, but will be + supported in 1.1 final. </li> + + <li>-XserializableAspects: Since perthis and pertarget aspects + are not implemented in 1.1beta1, this option has no meaning in + 1.1beta1. The intention is for 1.1 final to implement this + option just like the 1.0.6 compiler. </li> + + <li>-XaddSafePrefix: This option will not be supported in 1.1 at + all because we're now always using (what we believe to be) safe + prefixes. </li> + + <li>-Xlint: <a href="#XLINT">Will be more useful in + 1.1</a>. </li> + </ul> + + <h3><a name="ERROR_MESSAGES">Many confusing error messages in + 1.1beta1</a></h3> + + <p>Building on the eclipse compiler has given us access to a very + sophisticated problem reporting system as well as highly optimized + error messages for pure Java code. Often this leads to noticably + better error messages than from ajc-1.0.6. However, when we don't + handle errors correctly this can sometimes lead to cascading error + messages where a single small syntax error will produce dozens of + other messages.</p> + + <p>Many error condition in beta1 are signalled by exception. You + shouldn't be surprised to see RuntimeException("unimplemented") + produced from perfectly reasonable programs that use features we + just haven't implemented yet.</p> + + + <h3><a name="EXCEPTION_CHECKING">No exception checking during + weaving in 1.1beta1</a></h3> + + <p>Advice that has an explicit throws clause needs to have that + throws clause checked during weaving for each join point that is + matched. This checking is not implemented in 1.1beta1 which can + lead to checked exceptions being thrown from places they are not + allowed by the Java language.</p> + + <pre> before() throws IOException : execution (void m()) { + InputStream s = new FileInputStream("m.out"); + ... + } + ... + public void m() { ... } + </pre> + + <p>This code should result in a link-time weaving error that the + throws clause in the advice is incompatible with the checked + exceptions which can be legally thrown from the matched join + point. In beta1 this will just silently weave in the advice and + it will be possible for an IOException to be thrown from m().</p> + + <h3><a name="XLINT">-Xlint option partially supported in + 1.1beta1</a></h3> + + <p>-Xlint:ignore,error,warning will set the level for all Xlint + warnings. -Xlintfile:lint.properties allows fine-grained control. + In tools.jar, see org/aspectj/weaver/XlintDefault.properties for + the default behavior and a template to copy. </p> + + <p> In 1.1beta1, only two cases will signal Xlint warnings. We + expect this list to become much larger before 1.1final. Because + the configurability allows users to turn off warnings, we will + also be able to warn about more potentially dangerous situations, + such as the potentially unsafe casts used by very polymorphic uses + of proceed in around advice. + </p> + + <h3><a name="NO_SOURCE">Source-specific options</a></h3> + + <p> Because AspectJ 1.1beta1 does not generate source code after + weaving, the source-code-specific options -source, -usejavac, + -nocomment and -workingdir options are meaningless and so not + supported. </p> + + <h3><a name="NO_STRICT_LENIENT">The -strict and -lenient + options</a></h3> + + <p> Because AspectJ 1.1 uses the Eclipse compiler, which has its + own mechanism for changing strictness, we no longer support the + -strict and -lenient options. </p> + + <h3><a name="NO_PORTING">The -porting option</a></h3> + + <p> AspectJ 1.1 does not have a -porting option. We believe that + many useful porting information will be presented by the -Xlint + option when it is enabled. </p> + + <h3><a name="13_REQUIRED">J2SE 1.3 required</a></h3> + <p>Because we build on Eclipse, the compiler will no longer run under J2SE 1.2. + You must run the compiler (and all tools based on the compiler) + using J2SE 1.3 or later. </p> + +</body> </html>
\ No newline at end of file diff --git a/build/products/tools/dist/README-TOOLS.html b/build/products/tools/dist/README-TOOLS.html new file mode 100644 index 000000000..80f57081f --- /dev/null +++ b/build/products/tools/dist/README-TOOLS.html @@ -0,0 +1,323 @@ +<html> + +<head> +<meta http-equiv="Content-Language" content="en-us"> +<meta http-equiv="Content-Type" content="text/html; charset=windows-1252"> +<meta name="GENERATOR" content="Microsoft FrontPage 4.0"> +<meta name="ProgId" content="FrontPage.Editor.Document"> +<title>AspectJ Installation Note</title> +<STYLE TYPE="text/css"> +<!-- + + /* FOR THE SDA PAGE */ + + /* + BODY {margin-top: 15px; margin-left: 15px; margin-right: 15px;} + */ + + A:link { + color:#4756AC; + } + A:visited { + color:#60657B; + } + A:hover { + color:red + } + + INPUT {font:12px "Courier New", sans-serif;} + + H2 { + font:18px/18px Verdana, Arial, Helvetica, sans-serif; + color:black; + font-weight:bold; + margin-left: 10px; + line-height:110%; + } + H3 { + font:18px/18px Verdana, Arial, Helvetica, sans-serif; + color:black; + font-weight:bold; + margin-left: 10px; + line-height:110%; + } + H4 { + font:15px/16px Verdana, Arial, Helvetica, sans-serif; + color:black; + font-weight:bold; + margin-left: 10px; + line-height:140%; + } + P { + font:13px/13px Verdana, Arial, Helvetica, sans-serif; + margin-right: 10px; + margin-left: 10px; + line-height:130%; + } + .paragraph { + font:13px/13px Verdana, Arial, Helvetica, sans-serif; + margin-right: 10px; + margin-left: 10px; + line-height:130%; + } + .smallParagraph { + font:11px/11px Verdana, Arial, Helvetica, sans-serif; + margin-right: 10px; + margin-left: 10px; + line-height:130%; + } + /* + LI { + font:13px/13px Verdana, Arial, Helvetica, sans-serif; + text-align:justify; + margin-right: 10px; + margin-left: 15px; + line-height:120%; + } + + UL { + font:13px/13px Verdana, Arial, Helvetica, sans-serif; + text-align:justify; + margin-right: 10px; + margin-left: 15px; + line-height:120%; + }*/ + + DL { + font:13px/13px Verdana, Arial, Helvetica, sans-serif; + text-align:justify; + margin-right: 10px; + margin-left: 15px; + line-height:120%; + } + B { font:13px/13px Verdana, Arial, Helvetica, sans-serif; + font-weight:bold; + line-height:140%; + } + .footer { + font:10px/10px Verdana, Arial, Helvetica, sans-serif; + color:#888888; + text-align:left + } + .figureTitle { + font:13px/13px Verdana, Arial, Helvetica, sans-serif; + text-align:justify; + text-align:center + } + .copyrightNotice { + font:10px/10px Verdana, Arial, Helvetica, sans-serif; + color:#999999; + line-height:110%; + } + .smallHeading { + font:13px/13px Verdana, Arial, Helvetica, sans-serif; + font-weight:bold; + line-height:110%; + } + .tinyHeading { + font:11px/11px Verdana, Arial, Helvetica, sans-serif; + font-weight:bold; + line-height:120%; + } + .newsText { + font:11px/11px Verdana, Arial, Helvetica, sans-serif; + line-height:130%; + } + .smallParagraph { + font:11px/11px Verdana, Arial, Helvetica, sans-serif; + line-height:130%; + } + .fancyHeading { + font:20px/20px Chantilly, Arial, Helvetica, sans-serif; + margin-right: 10px; + color:#6f7a92; + margin-left: 10px; + line-height:130%; + } + +--> +</STYLE> +</head> + +<BODY BGCOLOR="white"> + +<h2 align="center">AspectJ<sup><small>TM</small></sup> Compiler and Core Tools</h2> + +<p align="center"><i>Version @build.version.long@ released on @build.date@.</i></p> + +<h3>1 Contents of this Package</h3> + +<UL> + <li>in the directory <CODE><aspectj install dir>/bin</CODE> + <ul> + <li><CODE>ajc</CODE>: compiler for the AspectJ language</li> + + <!-- XXX removed references to ajdoc and ajdb --> + <li><CODE>ajbrowser</CODE>: GUI for compiling programs with + <CODE>ajc</CODE> and navigating the + crosscutting structure</li> + </ul> + + <li>in the directory <CODE><aspectj install dir>/lib</CODE> + <ul> + <li><CODE>aspectjtools.jar</CODE>: libraries for the + AspectJ compiler, core tools, and Ant tasks</li> + + <li><CODE>aspectjrt.jar</CODE>: libraries for compiled + AspectJ code</li> + </ul> + </li> +</UL> + +<hr> + +<h3>2 Install Procedure</h3> + +<p>After finishing automatic installation, we recommend that you perform the +following steps to complete your installation:</p> + + <p><b>2.1 Add <CODE><aspectj install dir>/lib/aspectjrt.jar</CODE> to your class path</b></p> + +<blockquote> + <p>This .jar file contains several small classes that are + required to compile programs with the ajc compiler, and are the only + extra classes necessary to run programs compiled with the ajc + compiler. You must have these classes somewhere on your class path + when running programs compiled with ajc. For detailed instructions + please see the <a href="#configInstructions">Configuration + Instructions</a> at the bottom of this document.</p> +</blockquote> + + <p><b>2.2 Put the AspectJ <code>bin</code> directory on your PATH</b></p> + +<blockquote> + <p>Modify your PATH to include <code><b><aspectj install + dir>/bin</b></code>. This will make it easier to run ajc. + For detailed instructions please see the + <a href="#configInstructions">Configuration Instructions</a> at the + bottom of this document.</p> +</blockquote> + +<p><b>2.3 Review the documentation and examples </p> + +<blockquote> + <p>A tutorial, development and programming guide, and example programs + are available in <a href="docs/index.html">docs</a> + and <a href="examples/">examples</a> +</blockquote> + +<p>If you did not use the automatic installation process, you may wish +to create short launch scripts to start ajc easily (section 3).</p> + +<hr> + +<h3>3. Running the Tools</h3> + +<p>If you did not use the automatic installation process or the +default launch scripts do not work on your system, you may wish to +create short launch scripts to start ajc easily. </p> + +<p>The AspectJ compiler and tools are Java programs that you can +run directly. You can run the <code>aspectjtools.jar</code> directly +using java's -jar option: + +<blockquote> +<CODE>C:\jdk1.3\bin\java.exe -jar D:\aspectj\lib\aspectjtools.jar %*</CODE> +</blockquote> + +With no arguments or only argument list (.lst) files, this will launch +<code>ajbrowser</code>, the GUI structure browser; otherwise, it will +launch <code>ajc</code>, the AspectJ compiler. + +<p> You can also create scripts like those created by the installer. +These scripts use full paths that are system dependent so you will +likely have to change these. </p> + +<p>Here's a sample launch script for WinNT and Win2K (note that this +is single line in the .bat file):</p> + +<blockquote> +<CODE>C:\jdk1.3\bin\java.exe -classpath D:\aspectj\lib\aspectjtools.jar -Xmx64M +org.aspectj.tools.ajc.Main %*</CODE> +</blockquote> + +<p>Here's a sample launch script for a Unix shell (on Linux using Sun's JDK1.3):</p> + +<blockquote> +<CODE>/usr/java/jdk1.3/jre/bin/java -classpath /home/aspectj/lib/aspectjtools.jar -Xmx64M org.aspectj.tools.ajc.Main "$@"</CODE> +</blockquote> + +<hr> +<h3><a name="configInstructions">4. Configuration Instructions</a></h3> + +<h4>4.1 Adding <code><aspectj install dir>/lib/aspectjrt.jar</code> to your classpath</h4> + +<p>There are several ways to add this jar file to your classpath:</p> + +<ul> + <li> + copy <code>aspectjrt.jar</code> to the <code>jdk/jre/lib/ext</code> directory</li> + <li> + add <code>aspectjrt.jar</code> to your CLASSPATH + environment variable (see the next section for details)</li> + <li> + always use the "<code>-classpath aspectjrt.jar</code>" option when running + programs compiled with ajc</li> +</ul> +<h4>4.2 <A NAME="6.1">Setting the Environment Variables on Windows</A></h4> + +<P>The following instructions use the PATH variable as an example, but +this process is identical for the CLASSPATH variable. </P> + +<P>You can do the variables permanently for all the shells that you +will run or temporarily only for the current shell. To change the +PATH only in the current shell, and assuming you've installed AspectJ +in <code>C:\apps\aspectj</code>, type:</P> + +<blockquote><code>> set PATH=%PATH%;C:\apps\aspectj\bin</code></blockquote> + +<P><b>Changing Environment Variables Permanently on WinNT and Win2000</b> +<ul> + <li> + <p class="paragraph">open the Environment Variables dialog + <ul> + <li> + <p class="paragraph">WinNT: in "Control Panels" double-click + "System" and select the "Environment" tab</li> + <li> + <p class="paragraph">Win2K: in "Control Panels" double-click + "System", select the "Advanced" tab and click the + "Environment Variables..." button</li> + </ul> + </li> + <li> + <p class="paragraph">select the environment variable for editing, or add it + using the "New..." button if it does not exist</li> + <li> + <p class="paragraph">add the new entry separated from the others by a + semi-colon (;) and close the dialog</li> + <li> + <p class="paragraph">note that the change will not affect shells that were + already running </li> +</ul> +<P><b>Changing Environment Variables Permanently on Win9x</b> +<ul> + <li> + <p class="paragraph">open the AUTOEXEC.BAT with an editor such as NotePad</li> + +<LI> + <p class="paragraph">edit the PATH statement to include the new entry and save + the file, e.g.<BR> + +<blockquote> + <CODE>PATH C:\WINDOWS;C:\WINDOWS\COMMAND;C:\;C:\DOS;C:\apps\aspectj\bin</CODE> + </blockquote> +</LI> +<LI> + <p class="paragraph">note that the change will not affect shells that were + already running +</ul> + +</body> + +</html> diff --git a/build/products/tools/dist/lib/aspectjrt.jar b/build/products/tools/dist/lib/aspectjrt.jar new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/build/products/tools/dist/lib/aspectjrt.jar diff --git a/build/products/tools/dist/lib/aspectjtools.jar b/build/products/tools/dist/lib/aspectjtools.jar new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/build/products/tools/dist/lib/aspectjtools.jar diff --git a/build/products/tools/install/configure-auto.html b/build/products/tools/install/configure-auto.html new file mode 100644 index 000000000..7abdaac6b --- /dev/null +++ b/build/products/tools/install/configure-auto.html @@ -0,0 +1,17 @@ +<html> + +<head> +<title>auto configure</title> +</head> + +<body> + +<p>The installer has successfully found the path to your Java home (J2SE 1.3 or greater). +This path will be used as the default Java location when generating +script files for launching the AspectJ compiler and core tools. Unless +you know that this path is wrong, we suggest that you press +<b>Next</b> to continue.</p> + +</body> + +</html> diff --git a/build/products/tools/install/configure-hand.html b/build/products/tools/install/configure-hand.html new file mode 100644 index 000000000..73b3e2185 --- /dev/null +++ b/build/products/tools/install/configure-hand.html @@ -0,0 +1,21 @@ +<html> + +<head> +<title>configure</title> +</head> + +<body> + +<p>The installer cannot find the path to your Java home (J2SE 1.3 or greater). +Please try to find this directory on your path now. It will probably look +something like <code>jdk1.3.1</code>. If you can't find this +directory now, you may continue with the install, but you will have +to either edit your launch scripts by hand or to set the JAVA_HOME +environment variable to point to the right location. +</p> + +<p>Press <b>Next</b> when ready to continue.</p> + +</body> + +</html> diff --git a/build/products/tools/install/finish.html b/build/products/tools/install/finish.html new file mode 100644 index 000000000..5e7b60c60 --- /dev/null +++ b/build/products/tools/install/finish.html @@ -0,0 +1,26 @@ +<html> + +<head> +<title>finish</title> +</head> + +<body> + +<p>The automatic installation process is complete. There are still three +steps that we recommend you perform to complete the installation:</p> +<ol> + <li>Put <b><code>${installer.output.aspectjrt}</code></b> on your CLASSPATH. + This 29KB .jar file contains classes that are required by any program compiled + with the ajc compiler.</li> + <li>Modify your PATH to include <code> <b>${installer.output.dir.bin}</b></code>. + This will make it easier to run ajc. </li> + <li>Download the documentation and examples from http://aspectj.org + to learn how to use the language.</li> +</ol> + +<p>These steps are described in more detail in <code> +<b>${installer.output.readme}</b></code>.</p> + +</body> + +</html> diff --git a/build/products/tools/install/install-finish.html b/build/products/tools/install/install-finish.html new file mode 100644 index 000000000..26aea1e5e --- /dev/null +++ b/build/products/tools/install/install-finish.html @@ -0,0 +1,20 @@ +<html> + +<head> +<meta http-equiv="Content-Language" content="en-us"> +<meta http-equiv="Content-Type" content="text/html; charset=windows-1252"> +<meta name="GENERATOR" content="Microsoft FrontPage 4.0"> +<meta name="ProgId" content="FrontPage.Editor.Document"> +<title>New Page 1</title> +</head> + +<body> + +<p> </p> + +<p>The automatic installation process is complete. Press <b>Next </b>for +some important final instructions.</p> + +</body> + +</html> diff --git a/build/products/tools/install/install-start.html b/build/products/tools/install/install-start.html new file mode 100644 index 000000000..858ed8512 --- /dev/null +++ b/build/products/tools/install/install-start.html @@ -0,0 +1,21 @@ +<html> + +<head> +<meta http-equiv="Content-Language" content="en-us"> +<meta http-equiv="Content-Type" content="text/html; charset=windows-1252"> +<meta name="GENERATOR" content="Microsoft FrontPage 4.0"> +<meta name="ProgId" content="FrontPage.Editor.Document"> +<title>New Page 1</title> +</head> + +<body> + +<p> </p> + +<p>Now installing to ${installer.output.dir}.</p> + +<p>Press <b>Cancel</b> to interrupt the installation.</p> + +</body> + +</html> diff --git a/build/products/tools/install/intro.html b/build/products/tools/install/intro.html new file mode 100644 index 000000000..d64b62523 --- /dev/null +++ b/build/products/tools/install/intro.html @@ -0,0 +1,28 @@ +<html> + +<head> +<meta http-equiv="Content-Language" content="en-us"> +<meta http-equiv="Content-Type" content="text/html; charset=windows-1252"> +<meta name="GENERATOR" content="Microsoft FrontPage 4.0"> +<meta name="ProgId" content="FrontPage.Editor.Document"> +<title>New Page 1</title> +</head> + +<body> + +<h2 align="center">Installer for AspectJ(TM) Compiler and Core Tools</h2> + +<p align="center">Version ${build.version.long} built on ${build.date}</p> +<p>These tools are covered by the Mozilla Public License (see +http://aspectj.org/MPL). +This is a binary only release. You can download the source code at +http://aspectj.org</p> +<p>${copyright.allRights.from1998}</p> +<p></p> + +<p>Press <b>Next</b> to continue. At any time you may press <b>Cancel</b> +to exit the installation process.</p> + +</body> + +</html> diff --git a/build/products/tools/install/location.html b/build/products/tools/install/location.html new file mode 100644 index 000000000..1ac230abf --- /dev/null +++ b/build/products/tools/install/location.html @@ -0,0 +1,20 @@ +<html> + +<head> +<meta http-equiv="Content-Language" content="en-us"> +<meta http-equiv="Content-Type" content="text/html; charset=windows-1252"> +<meta name="GENERATOR" content="Microsoft FrontPage 4.0"> +<meta name="ProgId" content="FrontPage.Editor.Document"> +<title>New Page 1</title> +</head> + +<body> + +<p>Please select a directory into which to install + the AspectJ compiler and core tools.</p> +<p>Press <b>Install</b> to begin the installation process + to this directory.</p> + +</body> + +</html> diff --git a/build/scripts/lcp.bat b/build/scripts/lcp.bat new file mode 100644 index 000000000..24eee9b3d --- /dev/null +++ b/build/scripts/lcp.bat @@ -0,0 +1,2 @@ +set LCP=%LCP%;%1
+
diff --git a/build/scripts/localSetup.bat.txt b/build/scripts/localSetup.bat.txt new file mode 100644 index 000000000..b2b738334 --- /dev/null +++ b/build/scripts/localSetup.bat.txt @@ -0,0 +1,19 @@ + +rem sample local setup, e.g., for quicklook.bat +rem set values and change filename to localSetup.bat + +rem -- picked up by ant scripts +set JAVA_HOME=c:\apps\jdk1.3.1 + +rem -- quicklook.bat adds the following to command line + +rem setup from/to for result email +set userEmail=isberg@parc.com +set mailHost=dagobah.parc.xerox.com + +rem setup ant cvs task, which execs cvs +set HOME=c:\home\wes +set CVSROOT=:ext:isberg@aspectj.org:/aspectj-home/cvsroot +set CVS_RSH=c:\apps\ssh\ssh +set PATH=c:\home\apps\cygwin\bin;c:\WIN2000\system32;c:\WIN2000 + diff --git a/build/scripts/quicklook.bat b/build/scripts/quicklook.bat new file mode 100644 index 000000000..7ac007257 --- /dev/null +++ b/build/scripts/quicklook.bat @@ -0,0 +1,99 @@ +rem be verbose, no @echo off
+rem requires existing ajhome install for ant scripts, libs
+rem beware - withou DOS linefeeds, DOS won't interpret...
+
+rem ------ read variables from local setup
+set scriptPath=%~dp0
+set scriptDir=%scriptPath:\quicklook.bat=%
+if not exist %scriptDir%\localSetup.bat goto ERR_SETUP
+
+call %scriptDir%\localSetup.bat
+
+if "x" == "x%userEmail%" goto ERR_VARIABLE
+if "x" == "x%mailHost%" goto ERR_VARIABLE
+if "x" == "x%HOME%" goto ERR_VARIABLE
+if "x" == "x%CVS_RSH%" goto ERR_VARIABLE
+if "x" == "x%CVSROOT%" goto ERR_VARIABLE
+
+set buildDir=%scriptDir:\scripts=%
+set antScript=%buildDir:\build=%\lib\ant\bin\ant.bat
+
+if not exist %antScript% goto ERR_ANT
+if not exist %buildDir% goto ERR_BUILDDIR
+if not exist %JDKDIR% goto ERR_JDKDIR
+
+rem XXX redo these when web building
+rem set SQEDIRPATH=%SQEDRIVE%%SQEDIR%
+rem if exist %SQEDIRPATH% goto RUN
+rem net use %SQEDRIVE% \\samba\aop /persistent:no
+rem if not exist %SQEDIRPATH% goto ERR_MOUNT
+rem set mountedDrive=yes
+goto RUN
+
+rem build update.tree, quicklook
+:RUN
+if not "x" == "x%DEBUG%" set verbose=-verbose
+chdir %buildDir% || goto ERR_CD
+rem fyi, normal ant.bat script loses any internal "=", so we rely on a patched script +set MAIL_OPTIONS=-logger org.apache.tools.ant.listener.MailLogger -DMailLogger.mailhost=%mailHost% -DMailLogger.success.to=%userEmail% -DMailLogger.failure.to=%userEmail% -DMailLogger.from=%userEmail%
+set options=%MAIL_OPTIONS% -DCVSROOT=%CVSROOT%
+rem cannot use "|| goto ERR_ANT_UPDATE" b/c ant script returns non-zero for valid builds
+call %antScript% -f build.xml update.tree %verbose% %options% +
+set options=%MAIL_OPTIONS% -Duser.email=%userEmail% -Daop.dir=%SQEDRIVE%
+set options=%options% -Drun.ajcTests=runAjcTests -Drun.junit.tests=runJUnitTests
+call %antScript% -f build.xml quicklook %verbose% %options% +
+if "ok%mountedDrive%" == "okyes" net use %SQEDRIVE% /delete
+goto QUIT
+
+rem ---------- errors
+:ERR_CD
+echo "unable to cd to build directory: %buildDir%"
+goto QUIT
+
+:ERR_SETUP
+echo "expected local setup in %scriptDir%\localSetup.bat"
+goto QUIT
+
+:ERR_BUILDDIR
+echo "expected build dir: %buildDir%"
+goto QUIT
+
+:ERR_VARIABLE
+echo "local setup is incorrect - missing variables"
+goto QUIT
+
+:ERR_ANT
+echo "expected ant script: %antScript%"
+goto QUIT
+
+:ERR_JDKDIR
+echo "no JDKDIR=%JDKDIR%"
+goto QUIT
+
+:ERR_MOUNT
+echo "unable to mount or use SQEDIRPATH=%SQEDIRPATH%"
+goto QUIT
+
+:ERR_CREATE_FAILED
+echo "unable to find quicklook source after running setup build"
+goto QUIT
+
+:ERR_ANT_CREATE
+echo "FAIL ant create.source failed"
+goto QUIT
+
+:ERR_ANT_UPDATE
+echo "FAIL ant update.source failed"
+goto QUIT
+
+:ERR_ANT_QUICKLOOK
+echo "FAIL ant quicklook failed"
+goto QUIT
+
+:ERR_ANT_DESTORY
+echo "FAIL ant destroy.source failed"
+goto QUIT
+
+:QUIT
diff --git a/build/scripts/runQuicklook.bat b/build/scripts/runQuicklook.bat new file mode 100644 index 000000000..841bb57ea --- /dev/null +++ b/build/scripts/runQuicklook.bat @@ -0,0 +1,7 @@ +rem @echo off
+
+rem %~dp0 is name of current script
+set scriptPath=%~dp0
+set scriptDir=%scriptPath:\runQuicklook.bat=%
+
+call %scriptDir%quicklook.bat > %scriptDir%quicklook.out 2>&1
diff --git a/build/src/$installer$/org/aspectj/Main.java b/build/src/$installer$/org/aspectj/Main.java new file mode 100644 index 000000000..b7cdda94e --- /dev/null +++ b/build/src/$installer$/org/aspectj/Main.java @@ -0,0 +1,1699 @@ +/* ******************************************************************* + * Copyright (c) 2000-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + + + +package $installer$.org.aspectj; + +import javax.swing.*; +import javax.swing.border.*; +import java.awt.*; +import java.awt.event.*; +import java.io.*; +import java.net.URL; +import java.util.zip.*; +import java.util.*; +import java.lang.reflect.InvocationTargetException; + +/** + * Invoke the Installer gui. + * To run without gui, pass two arguments: + * <li> "-text".equals(arg[0])</li> + * <li> arg[1] is the path to a properties file which defines + * name="output.dir" value="{path to output dir}" + * name="context.javaPath" value="{path to JDKDIR}", i.e,. + * <pre>output.dir=c:/latest + * "context.javaPath=c:/apps/jdk1.3.1</pre></li> + * <li>outputDir must be created and empty (i.e., no overwriting</li> + * <li>the VM being invoked should be the target vm</li> + */ +public class Main { + public static void main(String[] args) { + Options.loadArgs(args); + boolean hasGui = (null == Options.textProperties); + Properties properties = new Properties(); + InputStream istream = null; + try { + istream = Main.class.getResourceAsStream(Installer.RESOURCE_DIR + "/properties.txt"); + if (istream == null) { + System.err.println("unable to load properties.txt using Main.class - exiting"); + Main.exit(-1); + } + properties.load(istream); + if (!hasGui) { + istream.close(); + istream = new FileInputStream(Options.textProperties); + properties.load(istream); + } + istream.close(); + istream = null; + } catch (IOException ioe) { + handleException(ioe); + } finally { + if (null != istream) { + try {istream.close();} + catch (IOException e){} // ignore + } + } + + try { + String className = (String)properties.get("installer.main.class"); + //UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + Installer installer = (Installer)Class.forName(className).newInstance(); + InstallContext installerContext = new InstallContext(properties); + installerContext.setHasGui(hasGui); + installer.setContext(installerContext); + if (installerContext.hasGui()) { // let context force whether or not to run gui + installer.runGUI(); + } else { + // set output dir and java path in context after minimal validation + String propName = "output.dir"; + String propValue = properties.getProperty(propName); + if (null == propValue) { + throw new Exception("expecting property " + propName); + } + String outputDirName = propValue; + propName = "context.javaPath"; + propValue = properties.getProperty(propName); + if (null == propValue) { + throw new Exception("expecting property " + propName); + } + String javaPath = propValue; + File outputDir = new File(outputDirName); + if (! outputDir.isDirectory()) { + throw new Exception("not a dir outputDirName: " + outputDirName + + " dir: " + outputDir); + } + if (! outputDir.canWrite()) { + throw new Exception("cannot write outputDirName: " + outputDirName + + " dir: " + outputDir); + } + InstallContext context = installer.getContext(); // todo: why not use installerContext? + context.setOutputDir(outputDir); + context.javaPath = new File(javaPath); + // todo: check javaPath for ... bin/java? lib/rt.jar? + if (! outputDir.isDirectory() || ! outputDir.canRead()) { + throw new Exception("invalid javaPath: " + javaPath); + } + // directly set context and run + installer.getInstallPane().setContext(installerContext); + installer.run(); + } + } catch (Exception e) { + handleException(e); + } + } + + public static void handleException(Throwable e) { + System.out.println("internal error: " + e.toString()); + e.printStackTrace(); + Main.exit(-1); + } + + /** indirection for System.exit - todo apply cleanup here as necessary */ + public static void exit(int value) { + System.exit(value); + } +} // class Main + +class Options { + public static boolean verbose = false; + public static String textProperties = null; + public static boolean forceError1 = false; + public static boolean forceError2 = false; + public static boolean forceHandConfigure = false; + + public static void loadArgs(String[] args) { + if (args == null) return; + for (int i=0; i<args.length; i++) { + String arg = args[i]; + if (arg == null) continue; + + if (arg.equals("-verbose")) verbose = true; + else if (arg.equals("-forceError1")) forceError1 = true; + else if (arg.equals("-forceError2")) forceError2 = true; + else if (arg.equals("-forceHandConfigure")) forceHandConfigure = true; + else if (arg.equals("-text")) { + if (i+1 < args.length) { + textProperties = args[++i]; + } + } + } + } +} + +class ToolsInstaller extends Installer { + public String getTitle() { return "AspectJ(TM) Compiler and Core Tools Installer"; } + + public String getPrefix() { return "tools"; } + + public ToolsInstaller() { + InstallPane installPane = new InstallPane(true); + setInstallPane(installPane); + panes = new WizardPane[] { + new IntroPane(), + new ConfigureLauncherPane(), + new LocationPane(), + installPane, + new FinishPane() + }; + } +} + +class DocsInstaller extends Installer { + public String getTitle() { return "AspectJ(TM) Documentation and Examples Installer"; } + + public String getPrefix() { return "docs"; } + + public DocsInstaller() { + InstallPane installPane = new InstallPane(false); + setInstallPane(installPane); + panes = new WizardPane[] { + new IntroPane(), + new LocationPane(), + installPane, + new FinishPane() + }; + } +} + +class AJDEForJBuilderInstaller extends Installer { + public String getTitle() { return "AspectJ(TM) Support for JBuilder"; } + + public String getPrefix() { return "ajdeForJBuilder"; } + + public AJDEForJBuilderInstaller() { + InstallPane installPane = new InstallPane(false); + setInstallPane(installPane); + panes = new WizardPane[] { + new IntroPane(), + new LocationPane() { + public String getDefaultLocation() { + if (context.onWindows()) { + // check some default locations + String[] paths = { "c:\\JBuilder6\\lib\\ext", + "c:\\apps\\JBuilder6\\lib\\ext", + "c:\\Program Files\\JBuilder6\\lib\\ext" }; + int pathIndex = 0; + for (; pathIndex < paths.length; pathIndex++) { + if (new File(paths[pathIndex]).exists()) return paths[pathIndex]; + } + return "c:\\JBuilder6\\lib\\ext"; + } else { + return "/usr/JBuilder6/lib/ext"; + } + } + + /** + * Make sure that the old jar file gets removed. + */ + public void verify() { + File jbuilder = new File(location.getText() + "/../../lib/jbuilder.jar"); + if (!jbuilder.exists() && hasGui()) { + int ret = JOptionPane.showConfirmDialog + (frame, + "The location you specified does not seem to be a " + + "valid JBuilder install directory." + + " Continue?", "Confirm Install", + JOptionPane.YES_NO_OPTION, + JOptionPane.QUESTION_MESSAGE); + if (ret != JOptionPane.YES_OPTION) { + Main.exit(-1); + } else { + // do nothing + } + } + + File oldFile = new File(location.getText() + "/ajbuilder.jar"); + if (oldFile.exists() && hasGui()) { + int ret = JOptionPane.showConfirmDialog(frame, + "This old version of AJDE for JBuilder (\"ajbuilder.jar\") exists" + + " and must be removed from the install directory." + + " OK to delete?", "Confirm Delete", + JOptionPane.YES_NO_OPTION, + JOptionPane.QUESTION_MESSAGE); + if (ret != JOptionPane.YES_OPTION) { + Main.exit(-1); + } else { + oldFile.delete(); + } + } + } + }, + installPane, + new FinishPane() + }; + } +} + +class AJDEForForteInstaller extends Installer { + public String getTitle() { return "AspectJ(TM) Support for Forte 4J"; } + + public String getPrefix() { return "ajdeForForte"; } + + private String installLoc = ""; + + public AJDEForForteInstaller() { + InstallPane installPane = new InstallPane(false); + setInstallPane(installPane); + panes = new WizardPane[] { + new IntroPane(), + new LocationPane() { + public String getDefaultLocation() { + if (context.onWindows()) { + // check some default locations + String[] paths = { "c:\\forte4j\\modules", + "c:\\apps\\forte4j\\modules", + "c:\\Program Files\\forte4j\\modules" }; + int pathIndex = 0; + for (; pathIndex < paths.length; pathIndex++) { + if (new File(paths[pathIndex]).exists()) return paths[pathIndex]; + } + return "c:\\forte4j\\modules"; + } else { + return "/usr/forte4j/modules"; + } + } + + public void verify() { + File forte = new File(location.getText() + "/../lib/openide.jar"); + installLoc = location.getText(); + if (!forte.exists() && hasGui()) { + int ret = + JOptionPane.showConfirmDialog + (frame, + "The location you specified does not seem to be a " + + "valid Forte install directory." + + " Continue?", "Confirm Install", + JOptionPane.YES_NO_OPTION, + JOptionPane.QUESTION_MESSAGE); + if (ret != JOptionPane.YES_OPTION) { + Main.exit(-1); + } else { + // do nothing + } + } + } + }, + installPane, + new FinishPane() { + public void finalActions() { // todo verify dir ../lib/ext exists? + // !!! this should be done with two install locations, not by moving a file + new File(installLoc + "/../lib/ext/aspectjrt.jar").delete(); + new File(installLoc + "/aspectjrt.jar").renameTo(new File((installLoc + "/../lib/ext/aspectjrt.jar"))); + new File(installLoc + "/aspectjrt.jar").delete(); + } + } + }; + } +} + +class SrcInstaller extends Installer { + public String getTitle() { return "AspectJ(TM) Compiler and Core Tools Sources Installer"; } + + public String getPrefix() { return "sources"; } + + public SrcInstaller() { + InstallPane installPane = new InstallPane(false); + setInstallPane(installPane); + panes = new WizardPane[] { + new IntroPane(), + new LocationPane(), + installPane, + new FinishPane() + }; + } +} + + +abstract class Installer { + static final String EXIT_MESSAGE = "Are you sure you want to cancel the installation?"; + static final String EXIT_TITLE = "Exiting installer"; + /** relative directory in jar from package $installer$.org.aspectj + for loading resources - todo must be tracked during build */ + public static final String RESOURCE_DIR = "resources"; + + JFrame frame; + InstallContext context; + /** special pane that actually does the installation */ + InstallPane installPane; + + public Installer() { + } + + protected void setInstallPane(InstallPane installPane) { + this.installPane = installPane; + } + public InstallPane getInstallPane() { + return installPane; + } + + /** directly run the install pane, if any */ + public void run() { + if (null != installPane) { + installPane.run(); + } + } + + public abstract String getPrefix(); + + public void setContext(InstallContext context) { + this.context = context; + context.installer = this; + } + + public InstallContext getContext() { + return context; + } + + public String getTitle() { return "AspectJ(TM) Installer"; } + public int getWidth() { return 640; } + public int getHeight() { return 460; } + + protected WizardPane[] panes = new WizardPane[0]; + public WizardPane[] getPanes() { + return panes; + } + + public int findPaneIndex(WizardPane pane) { + for (int i = 0; i < panes.length; i++) { + if (panes[i] == pane) return i; + } + return -1; + } + + Component header, footer, body; + + public void runGUI() { + frame = new JFrame(getTitle()); + + if (Options.forceError1) { + throw new RuntimeException("forced error1 for testing purposes"); + } + + Dimension size = Toolkit.getDefaultToolkit().getScreenSize(); + + int x = (int)(size.getWidth() - getWidth()) / 2; + int y = (int)(size.getHeight() - getHeight()) / 2; + + //include a few sanity checks on starting position + if (x < 0) x = 0; + if (x > 600) x = 600; + if (y < 0) y = 0; + if (y > 400) y = 400; + + frame.setLocation(x, y); + frame.setSize(getWidth(), getHeight()); + moveToPane(getPanes()[0]); + frame.setVisible(true); + } + + public void moveToPane(WizardPane pane) { + pane.setContext(this.context); + + Dimension size = frame.getContentPane().getSize(); + + JPanel contents = new JPanel(); + contents.setLayout(new BorderLayout()); + header = makeHeader(); + contents.add(header, BorderLayout.NORTH); + + body = pane.getPanel(); + contents.add(body, BorderLayout.CENTER); + + footer = pane.getButtons(); + contents.add(footer, BorderLayout.SOUTH); + + contents.revalidate(); + contents.setSize(size); + + frame.setContentPane(contents); + + //XXX deal with threading here? + pane.run(); + } + + public Icon loadImage(String name) { + return new javax.swing.ImageIcon(this.getClass().getResource(name)); + } + + public Component makeHeader() { + return new JLabel(loadImage(Installer.RESOURCE_DIR + "/aspectjBanner.gif")); + } + + public ActionListener makeNextAction(final WizardPane pane) { + int nextPaneIndex = findPaneIndex(pane) + 1; + if (nextPaneIndex >= getPanes().length) { + return null; + } + + final WizardPane nextPane = getPanes()[nextPaneIndex]; + return new ActionListener() { + public void actionPerformed(ActionEvent e) { + pane.finish(); + moveToPane(nextPane); + } + }; + } + + public ActionListener makeBackAction(final WizardPane pane) { + int nextPaneIndex = findPaneIndex(pane) - 1; + if (nextPaneIndex < 0) { + return null; + } + + final WizardPane nextPane = getPanes()[nextPaneIndex]; + return new ActionListener() { + public void actionPerformed(ActionEvent e) { + moveToPane(nextPane); + } + }; + } + + public ActionListener makeCancelAction(WizardPane pane) { + return new ActionListener() { + public void actionPerformed(ActionEvent e) { + int ret = JOptionPane.showConfirmDialog(frame, EXIT_MESSAGE, EXIT_TITLE, + JOptionPane.YES_NO_OPTION, + JOptionPane.QUESTION_MESSAGE); + if (ret == JOptionPane.YES_OPTION) { + Main.exit(-1); + } + } + }; + } + public ActionListener makeFinishAction(WizardPane pane) { + return new ActionListener() { + public void actionPerformed(ActionEvent e) { + Main.exit(0); + } + }; + } +} + + + + // willing to go up to 3 levels deep to find either jre or jdk + + // jre\[*\]lib\ext + // jdk*\lib\tools.jar + + /***** + final static int MAX_DEPTH = 4; + public static void findPaths(String prefix, File currentDir, int currentDepth) { + if (currentDepth > MAX_DEPTH) return; + if (!currentDir.exists() || !currentDir.isDirectory()) return; + File [] files = currentDir.listFiles(); + if (files == null) return; + for (int i=0; i<files.length; i++) { + if (files[i] == null) continue; + if (!files[i].isDirectory()) continue; + if (files[i].getName().startsWith(prefix)) { + System.out.println("found: " + files[i]); + } else { + findPaths(prefix, files[i], currentDepth + 1); + } + } + } + + public static void findPaths(String prefix) { + File [] files = File.listRoots(); + for (int i=1; i<files.length; i++) { + if (!files[i].isDirectory()) continue; + if (files[i].getName().toLowerCase().startsWith(prefix)) { + System.out.println("found: " + files[i]); + } else { + findPaths(prefix, files[i], 1); + } + } + } + *****/ + +class InstallContext { + public InstallContext(Map properties) { + this.properties = properties; + properties.put("user.home", System.getProperty("user.home")); + //System.out.println("new install context"); + } + + private File outputDir; + public void setOutputDir(File outputDir) { + this.outputDir = outputDir; + + properties.put("installer.output.dir", outputDir.getAbsolutePath()); + properties.put("installer.output.dir.bin", new File(outputDir, "bin").getAbsolutePath()); + properties.put("installer.output.dir.doc", new File(outputDir, "doc").getAbsolutePath()); + properties.put("installer.output.aspectjrt", new File(new File(outputDir, "lib"), "aspectjrt.jar").getAbsolutePath()); + properties.put("installer.output.readme", new File(outputDir, "README-" + installer.getPrefix().toUpperCase()+".html").getAbsolutePath()); + } + + public File getOutputDir() { + return outputDir; + } + + private boolean hasGui; + public File javaPath; + public File toolsJarPath; + + public Installer installer; + + private Map properties; + + public boolean hasGui() { + return hasGui; + } + + public void setHasGui(boolean hasGui) { + if (this.hasGui != hasGui) { + this.hasGui = hasGui; + } + } + + public Font getFont() { + return new Font("Serif", Font.PLAIN, 14); + } + + public String getOS() { + return System.getProperty("os.name"); + } + + public boolean onOS2() { + return getOS().equals("OS2") || getOS().equals("OS/2"); + } + + public boolean onWindows() { + return getOS().startsWith("Windows") || onOS2(); + } + + public boolean onWindowsPro() { + return getOS().equals("Windows NT") || getOS().equals("Windows 2000"); + } + + public boolean onMacintosh() { + return getOS().startsWith("Mac"); + } + + public boolean onUnix() { + return !onMacintosh() && !onWindows(); + } + + static final String[] TEXT_EXTENSIONS = { + ".txt", ".text", ".htm", ".html", ".java", ".ajava", "README", ".lst" + }; + + public boolean isTextFile(File file) { + String name = file.getName(); + + for (int i=0; i < TEXT_EXTENSIONS.length; i++) { + if (name.endsWith(TEXT_EXTENSIONS[i])) return true; + } + + return false; + } + + public void handleException(Throwable e) { + System.out.println("internal error: " + e.toString()); + e.printStackTrace(); + if (hasGui()) { + JOptionPane.showMessageDialog(installer.frame, e.toString(), + "Unexpected Exception", JOptionPane.ERROR_MESSAGE); + } + } + + final static String OVERWRITE_MESSAGE = "Overwrite file "; + final static String OVERWRITE_TITLE = "Overwrite?"; + + final static String[] OVERWRITE_OPTIONS = { + "Yes", "No", "Yes to all" //, "No to all" + }; + + final static int OVERWRITE_YES = 0; + final static int OVERWRITE_NO = 1; + final static int OVERWRITE_ALL = 2; + //final static int OVERWRITE_NONE = 3; + + int overwriteState = OVERWRITE_NO; + boolean shouldOverwrite(final File file) { + //System.out.println("overwrite: " + file + " state " + overwriteState); + if (overwriteState == OVERWRITE_ALL) return true; + //if (overwriteState == OVERWRITE_NONE) return false; + + try { + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + int ret = JOptionPane.showOptionDialog(installer.frame, + OVERWRITE_MESSAGE+file.getPath(), OVERWRITE_TITLE, + JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null, + OVERWRITE_OPTIONS, OVERWRITE_OPTIONS[OVERWRITE_YES]); + + overwriteState = ret; + } + }); + } catch (InvocationTargetException ite) { + handleException(ite.getTargetException()); + } catch (InterruptedException ie) { + } + + return overwriteState == OVERWRITE_YES || overwriteState == OVERWRITE_ALL; + } + + + public Map getProperties() { + return properties; + } +} + +abstract class WizardPane { + InstallContext context; + + protected JButton backButton = null; + protected JButton nextButton = null; + protected JButton cancelButton = null; + + public void setContext(InstallContext context) { this.context = context; } + + public abstract JPanel makePanel(); + + protected JTextArea makeTextArea(String data) { + JTextArea text = new JTextArea(data); + text.setOpaque(false); + text.setFont(context.getFont()); + text.setEditable(false); + return text; + } + + /** @return false only if there is an InstallContext saying there is no GUI */ + protected boolean hasGui() { + final InstallContext icontext = context; + return ((null == icontext) || icontext.hasGui()); + } + + public static String stringFromStream(InputStream stream) throws IOException { + BufferedReader reader = new BufferedReader(new InputStreamReader(stream, "US-ASCII")); + + StringBuffer ret = new StringBuffer(); + int data; + while ( (data = reader.read()) != -1) { + ret.append( (char)data ); + } + return ret.toString(); + } + + public static String removeHead(String text) { + int startIndex = text.indexOf("<head>"); + int stopIndex = text.indexOf("</head>"); + if (startIndex == -1 || stopIndex == -1) return text; + stopIndex += 7; + return text.substring(0, startIndex) + text.substring(stopIndex); + } + + static String styleHeader = "<head></head>";/*<STYLE TYPE=\"text/css\"><!--\n" + + " h2 {\n" + + " font-size: x-large;\n" + + " font-family: Serif;\n" + + " font-weight: normal;\n" + + " }\n" + + " p {\n" + + " font-family: Serif;\n" + + " font-weight: normal;\n" + + //" color:black;\n" + + "}</head>\n";*/ + + + public static String applyProperties(String text, Map map) { + // ${name} -> map.get(name).toString() + int lastIndex = 0; + StringBuffer buf = new StringBuffer(); + + int startIndex; + while ( (startIndex = text.indexOf("${", lastIndex)) != -1) { + int endIndex = text.indexOf('}', startIndex); + //XXX internal error here + if (endIndex == -1) break; + buf.append(text.substring(lastIndex, startIndex)); + String key = text.substring(startIndex+2, endIndex); + lastIndex = endIndex+1; + Object replaceText = map.get(key); + //System.out.println("key: " + key + " -> " + replaceText); + if (replaceText == null) replaceText = "NOT_FOUND"; + buf.append(replaceText.toString()); + } + buf.append(text.substring(lastIndex)); + + return buf.toString(); + } + + public String applyProperties(String text) { + return applyProperties(text, context.getProperties()); + } + + protected String loadText(String filename) { + String fullname = Installer.RESOURCE_DIR + "/" + filename; + //context.installer.getPrefix() + "-" + filename; + + try { + String text = stringFromStream(getClass().getResourceAsStream(fullname)); + text = styleHeader + removeHead(text); + text = applyProperties(text); + //System.out.println(text); + return text; + } catch (IOException e) { + context.handleException(e); + return ""; + } + } + + protected JEditorPane makeHTMLArea(String filename) { + JEditorPane editorPane = new JEditorPane("text/html", loadText(filename)); + /* { + public void paint(Graphics g) { + Graphics2D g2 = (Graphics2D)g; + g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + super.paint(g2); + } + };*/ + editorPane.setEditable(false); + editorPane.setOpaque(false); + return editorPane; + } + + protected void setHTMLArea(JEditorPane pane, String filename) { + pane.setText(loadText(filename)); + } + + + protected JPanel makeLocationBox(String label, JTextField textField, JButton browseButton) { + JPanel box = new JPanel(); + box.setLayout(new BoxLayout(box, BoxLayout.X_AXIS)); + + textField.setFont(context.getFont()); + textField.selectAll(); + box.add(textField); + + box.add(browseButton); + Border border = BorderFactory.createTitledBorder(label); + final int INSET = 8; + border = new CompoundBorder(border, new EmptyBorder(1, INSET, INSET, INSET)); + box.setBorder(border); + + return box; + } + + + private JPanel panel = null; + public JPanel getPanel() { + if (panel == null) { + panel = makePanel(); + panel.setBorder(BorderFactory.createEmptyBorder(0, 10, 0, 10)); + } + return panel; + } + + protected void setListener(JButton button, ActionListener listener) { + if (listener == null) { + button.setEnabled(false); + } else { + button.addActionListener(listener); + } + } + + protected Component makeButtons(Installer installer) { + JPanel panel = new JPanel(new FlowLayout(FlowLayout.RIGHT)); + + backButton = new JButton("Back"); + setListener(backButton, installer.makeBackAction(this)); + panel.add(backButton); + + nextButton = new JButton("Next"); + setListener(nextButton, installer.makeNextAction(this)); + panel.add(nextButton); //.setDefaultCapable(true); + + JLabel space = new JLabel(); + space.setPreferredSize(new Dimension(20, 0)); + panel.add(space); + + cancelButton = new JButton("Cancel"); + setListener(cancelButton, installer.makeCancelAction(this)); + panel.add(cancelButton); + + return panel; + } + + private Component buttons = null; + public Component getButtons() { + if (buttons == null) { + buttons = makeButtons(context.installer); + //buttons.setBorder(BorderFactory.createEmptyBorder(0, 10, 0, 10)); + } + context.installer.frame.getRootPane().setDefaultButton(nextButton); + return buttons; + } + + public void finish() { + if (Options.forceError2) { + throw new RuntimeException("forced error2 for testing purposes"); + } + } + public void run() {} +} + +class IntroPane extends WizardPane { + public JPanel makePanel() { + Component text = makeHTMLArea("intro.html"); + + JPanel panel = new JPanel(new BorderLayout()); + panel.add(text); + return panel; + } +} + +class FinishPane extends WizardPane { + public JPanel makePanel() { + Component text = makeHTMLArea("finish.html"); + + JPanel panel = new JPanel(new BorderLayout()); + panel.add(text); + finalActions(); + return panel; + } + + public Component makeButtons(Installer installer) { + Component ret = super.makeButtons(installer); + nextButton.setText("Finish"); + nextButton.setEnabled(true); + nextButton.addActionListener(installer.makeFinishAction(this)); + backButton.setEnabled(false); + cancelButton.setEnabled(false); + return ret; + } + + public void finalActions() { } +} + + +class LocationPane extends WizardPane implements ActionListener { + //XXX need more sophisticated default location finding + //XXX would like to find the place they last chose... + public String getDefaultLocation() { + if (context.onWindows()) { + //XXX hard-coded majorminor version needs to be fixed by 1.1 release + return "c:\\aspectj1.1"; + } else { + return new File(System.getProperty("user.home"), "aspectj1.1").getAbsolutePath(); + } + } + + protected JTextField location; + + public JPanel makePanel() { + Component text = makeHTMLArea("location.html"); + + location = new JTextField(getDefaultLocation()); + JButton browse = new JButton("Browse..."); + browse.addActionListener(this); + + JPanel locationBox = makeLocationBox("installation directory", location, browse); + + GridBagLayout bag = new GridBagLayout(); + GridBagConstraints c = new GridBagConstraints(); + + JPanel panel = new JPanel(bag); + c.fill = GridBagConstraints.BOTH; + c.weightx = 1.0; + c.gridwidth = GridBagConstraints.REMAINDER; + bag.setConstraints(text, c); + panel.add(text); + + c.weighty = 1.0; + c.fill = GridBagConstraints.HORIZONTAL; + c.gridwidth = GridBagConstraints.REMAINDER; + bag.setConstraints(locationBox, c); + panel.add(locationBox); + + //XXX set next button to read install + //context.nextButton.setText("Install"); + + return panel; + } + + public Component makeButtons(Installer installer) { + Component ret = super.makeButtons(installer); + nextButton.setText("Install"); + return ret; + } + + public void actionPerformed(ActionEvent e) { + JFileChooser chooser = new JFileChooser(); // { + // public void approveSelection() { + // System.out.println("approved selection"); + // } + //}; //field.getText()); + chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES); + int returnVal = chooser.showDialog(location, "Select"); + if(returnVal == JFileChooser.APPROVE_OPTION) { + File file = chooser.getSelectedFile(); + if (!file.isDirectory()) file = file.getParentFile(); + String name = file.getPath(); + location.setText(name); + location.selectAll(); + } + } + + /** + * Override to do any additional checks. + */ + protected void verify() {} + + public void finish() { + verify(); + context.setOutputDir(new File(location.getText())); + } +} + +class ConfigureLauncherPane extends WizardPane { + /* + //XXX check that the returned file is valid + public String getDefaultJavaLocation() { + String name = "java"; + if (context.onWindows()) name += ".exe"; + + if (Options.verbose) { + System.out.println("java.home: " + System.getProperty("java.home")); + System.out.println(" java: " + new File(new File(System.getProperty("java.home"), "bin"), name)); + System.out.println(" java: " + new File(new File(System.getProperty("java.home"), "bin"), name).getPath()); + } + + return new File(new File(System.getProperty("java.home"), "bin"), name).getPath(); + } + */ + + public String getDefaultJavaHomeLocation() { + if (!Options.forceHandConfigure) { + File javaHome = findJavaHome(); + if (javaHome != null) return javaHome.getPath(); + } + return null; + } + + public void chooseFile(JTextField field) { + JFileChooser chooser = new JFileChooser(); //field.getText()); + chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); + int returnVal = chooser.showDialog(field, "Select"); + if(returnVal == JFileChooser.APPROVE_OPTION) { + String name = chooser.getSelectedFile().getPath(); + field.setText(name); + field.selectAll(); + } + } + + + public ActionListener makeJavaLocationBrowseListener() { + return new ActionListener() { + public void actionPerformed(ActionEvent e) { + chooseFile(javaLocation); + } + }; + } + +// public ActionListener makeToolsJarLocationBrowseListener() { +// return new ActionListener() { +// public void actionPerformed(ActionEvent e) { +// chooseFile(toolsJarLocation); +// } +// }; +// } + + + private JTextField javaLocation; + //private JTextField toolsJarLocation; + + public JPanel makePanel() { + String javaPath = getDefaultJavaHomeLocation(); + //String toolsJarPath = getDefaultToolsJarLocation(); + + Component text; + if (javaPath == null) { + javaPath = "<java home not found>"; + text = makeHTMLArea("configure-hand.html"); + } else { + text = makeHTMLArea("configure-auto.html"); + } + + javaLocation = new JTextField(javaPath); + JButton javaLocationBrowse = new JButton("Browse..."); + javaLocationBrowse.addActionListener(makeJavaLocationBrowseListener()); + + JPanel javaLocationBox = makeLocationBox("java home directory", javaLocation, javaLocationBrowse); + + +// toolsJarLocation = new JTextField(toolsJarPath); +// JButton toolsJarLocationBrowse = new JButton("Browse..."); +// toolsJarLocationBrowse.addActionListener(makeToolsJarLocationBrowseListener()); + +// JPanel toolsJarLocationBox = makeLocationBox("full path to tools.jar", toolsJarLocation, toolsJarLocationBrowse); + + GridBagLayout bag = new GridBagLayout(); + GridBagConstraints c = new GridBagConstraints(); + + JPanel panel = new JPanel(bag); + c.fill = GridBagConstraints.BOTH; + c.weightx = 1.0; + c.weighty = 1.0; + //c.ipady = 10; + c.gridwidth = GridBagConstraints.REMAINDER; + bag.setConstraints(text, c); + panel.add(text); + + c.weighty = 0.0; + //c.fill = GridBagConstraints.VERTICAL; + bag.setConstraints(javaLocationBox, c); + panel.add(javaLocationBox); + +// c.weighty = 0.25; +// JLabel space = new JLabel(); +// bag.setConstraints(space, c); +// panel.add(space); + +// c.weighty = 0.0; +// bag.setConstraints(toolsJarLocationBox, c); +// panel.add(toolsJarLocationBox); + + c.weighty = 0.5; + JLabel space = new JLabel(); + bag.setConstraints(space, c); + panel.add(space); + + return panel; + } + + public void finish() { + context.javaPath = new File(javaLocation.getText()); +// context.toolsJarPath = new File(toolsJarLocation.getText()); + + //XXX need much more work on helping the user get these paths right +// if (context.javaPath.isDirectory()) { +// context.javaPath = new File(context.javaPath, "java"); +// } +// if (context.toolsJarPath.isDirectory()) { +// context.toolsJarPath = new File(context.toolsJarPath, "tools.jar"); +// } + } + + + //XXX add user.home to prefixes in a rational way + public static final String[] windowsPaths = { + "c:\\jdk", "c:\\apps\\jdk", "${user.home}\\jdk" + }; + + public static final String[] unixPaths = { + "/usr/local/bin/jdk", "/usr/bin/jdk", "/usr/bin/jdk", "${user.home}/jdk" + }; + + public static final String[] suffixes = { + "1.3.1", "1.3", "1.2", "13", "12", "2", "", "1.4" + }; + + public static boolean windows = true; + + public boolean isLegalJavaHome(File home) { + File bin = new File(home, "bin"); + return new File(bin, "java").isFile() || new File(bin, "java.exe").isFile(); + } + + public boolean isLegalJDKHome(File home) { + File lib = new File(home, "lib"); + return new File(lib, "tools.jar").isFile(); + } + + + public File findJavaHome() { + String s = System.getProperty("java.home"); + File javaHome = null; + if (s != null) { + javaHome = new File(s); + if (isLegalJDKHome(javaHome)) return javaHome; + if (isLegalJavaHome(javaHome)) { + File parent = javaHome.getParentFile(); + if (parent != null && isLegalJDKHome(parent)) return parent; + } + } + + String[] paths; + if (windows) { + paths = windowsPaths; + } else { + paths = unixPaths; + } + + for (int suffixIndex = 0; suffixIndex < suffixes.length; suffixIndex++) { + String suffix = suffixes[suffixIndex]; + for (int prefixIndex = 0; prefixIndex < paths.length; prefixIndex++) { + String prefix = paths[prefixIndex]; + prefix = applyProperties(prefix); + File test = new File(prefix+suffix); + if (isLegalJavaHome(test)) { + if (isLegalJDKHome(test)) return test; + else if (javaHome == null) javaHome = test; + } + } + } + return javaHome; + } +} + + +class InstallPane extends WizardPane { + private JProgressBar progressBar; + private JTextField progressItem; + private JEditorPane message; + + private boolean makeLaunchScripts = false; + + public InstallPane(boolean makeLaunchScripts) { + this.makeLaunchScripts = makeLaunchScripts; + } + + public JPanel makePanel() { + message = makeHTMLArea("install-start.html"); + + progressBar = new JProgressBar(); + + progressItem = new JTextField(); + progressItem.setOpaque(false); + progressItem.setFont(context.getFont()); + progressItem.setEditable(false); + + + GridBagLayout bag = new GridBagLayout(); + GridBagConstraints c = new GridBagConstraints(); + + JPanel panel = new JPanel(bag); + c.fill = GridBagConstraints.BOTH; + c.weightx = 1.0; + c.weighty = 1.0; + //c.ipady = 10; + c.gridwidth = GridBagConstraints.REMAINDER; + bag.setConstraints(message, c); + panel.add(message); + + c.weighty = 0.0; + //c.fill = GridBagConstraints.VERTICAL; + bag.setConstraints(progressBar, c); + panel.add(progressBar); + + c.weighty = 0.1; + JLabel space = new JLabel(); + bag.setConstraints(space, c); + panel.add(space); + + c.weighty = 0.0; + bag.setConstraints(progressItem, c); + panel.add(progressItem); + + c.weighty = 0.5; + space = new JLabel(); + bag.setConstraints(space, c); + panel.add(space); + + return panel; + } + + class InstallRunner implements Runnable { + public InstallRunner() {} + + public void run() { + try { + new CurrentJarUnpacker(context, InstallPane.this).unpack(Installer.RESOURCE_DIR + "/contents.txt", context.getOutputDir()); + + if (makeLaunchScripts) { + LaunchScriptMaker lsm = new LaunchScriptMaker(context); + lsm.writeScript("ajc"); + //lsm.writeScript("ajdoc"); + //lsm.writeScript("ajdb"); + lsm.writeScript("ajbrowser"); + } + if (hasGui()) { + progressBar.setValue(100); + setHTMLArea(message, "install-finish.html"); + } + } catch (IOException ioe) { + context.handleException(ioe); + } + + if (hasGui()) { + cancelButton.setEnabled(false); + nextButton.setEnabled(true); + } + } + } + + public Component makeButtons(Installer installer) { + Component ret = super.makeButtons(installer); + //nextButton.setText("Finish"); + nextButton.setEnabled(false); + //nextButton.addActionListener(installer.makeFinishAction(this)); + backButton.setEnabled(false); + return ret; + } + + + public void run() { + Thread thread = new Thread(new InstallRunner()); + thread.start(); + } + + public void progressMessage(final String message) { + if (!hasGui()) { + return; + } + try { + //XXX performance tradeoff between invokeAndWait and invokeLater... + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + progressItem.setText(message); + } + }); + } catch (InvocationTargetException ite) { + context.handleException(ite.getTargetException()); + } catch (InterruptedException ie) { + } + } + + int nBytes = 0; + int bytesWritten = 0; + public void progressBytesWritten(int bytes) { + if (!hasGui()) { + return; + } + bytesWritten += bytes; + final int PCT = (int)(100.0 * bytesWritten / nBytes); + //System.out.println("bytesWritten: " + bytesWritten); + try { + //XXX performance tradeoff between invokeAndWait and invokeLater... + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + progressBar.setValue(PCT); + } + }); + } catch (InvocationTargetException ite) { + context.handleException(ite.getTargetException()); + } catch (InterruptedException ie) { + } + } +} + +class CurrentJarUnpacker { + InstallContext context; + InstallPane installPane; + + public CurrentJarUnpacker(InstallContext context, InstallPane installPane) { + this.context = context; + this.installPane = installPane; + } + + public File makeOutputFile(String name, File outputFile) { + int index; + int lastIndex = 0; + + while ( (index = name.indexOf('/', lastIndex)) != -1) { + outputFile = new File(outputFile, name.substring(lastIndex, index)); + lastIndex = index + 1; + } + + return new File(outputFile, name.substring(lastIndex)); + } + + final static int BUF_SIZE = 4096; + + public void writeStream(InputStream zis, File outputFile) throws IOException { + if (outputFile.exists()) { + if (!context.shouldOverwrite(outputFile)) return; + } + + installPane.progressMessage("writing " + outputFile.getAbsolutePath()); + + outputFile.getParentFile().mkdirs(); + + if (context.isTextFile(outputFile)) { + writeTextStream(zis, outputFile); + } else { + writeBinaryStream(zis, outputFile); + } + } + + public void writeBinaryStream(InputStream zis, File outputFile) throws IOException { + byte[] buffer = new byte[BUF_SIZE]; + int nRead = 0; + + OutputStream os = new BufferedOutputStream(new FileOutputStream(outputFile)); + + while ( (nRead = zis.read(buffer)) != -1) { + os.write(buffer, 0, nRead); + installPane.progressBytesWritten(nRead); + } + os.close(); + } + + public void writeTextStream(InputStream zis, File outputFile) throws IOException { + BufferedWriter os = new BufferedWriter(new FileWriter(outputFile)); + BufferedReader r = new BufferedReader(new InputStreamReader(zis, "US-ASCII")); + + String l; + while ( (l = r.readLine()) != null) { + os.write(l); + os.newLine(); + installPane.progressBytesWritten(l.length() + 1); + } + os.close(); + } + + public void writeResource(String name, File outputDir) throws IOException { + File outputFile = makeOutputFile(name, outputDir); + //System.out.println("finding name: " + name); + writeStream(getClass().getResourceAsStream("/" + name), outputFile); + } + + public void unpack(String contentsName, File outputDir) throws IOException { + URL url = getClass().getResource(contentsName); + InputStream stream = url.openStream(); + BufferedReader reader = new BufferedReader(new InputStreamReader(stream, "US-ASCII")); + + String line = reader.readLine(); + installPane.nBytes = Integer.parseInt(line); + + while ( (line = reader.readLine()) != null) { + writeResource(line, outputDir); + } + + installPane.progressMessage("done writing"); + } +} + +class LaunchScriptMaker { + static final String toolsPackage = "org.aspectj.tools"; + + InstallContext context; + public LaunchScriptMaker(InstallContext context) { + this.context = context; + } + + private void writeWindowsHeader(String className, PrintStream ps) { + ps.println("@echo off"); + ps.println("REM This file generated by AspectJ installer"); + ps.println("REM Created on "+new java.util.Date()+" by "+ + System.getProperty("user.name")); + ps.println(""); + ps.println("if \"%JAVA_HOME%\" == \"\" set JAVA_HOME=" + + context.javaPath.getAbsolutePath()); + ps.println("if \"%ASPECTJ_HOME%\" == \"\" set ASPECTJ_HOME=" + + context.getOutputDir().getAbsolutePath()); + ps.println(""); + + ps.println("if exist \"%JAVA_HOME%\\bin\\java.exe\" goto haveJava"); + ps.println("if exist \"%JAVA_HOME%\\bin\\java.bat\" goto haveJava"); + ps.println("if exist \"%JAVA_HOME%\\bin\\java\" goto haveJava"); + + ps.println("echo java does not exist as %JAVA_HOME%\\bin\\java"); + ps.println("echo please fix the JAVA_HOME environment variable"); + ps.println(":haveJava"); + ps.println("\"%JAVA_HOME%\\bin\\java\" -classpath " + + "\"%ASPECTJ_HOME%\\lib\\aspectjtools.jar;%JAVA_HOME%\\lib\\tools.jar;%CLASSPATH%\""+ + " -Xmx64M " + className + //" -defaultClasspath " + "\"%CLASSPATH%\"" + + " " + makeScriptArgs(false)); + } + + private void writeUnixHeader(String className, PrintStream ps) { + File binsh = new File(File.separator+"bin", "sh"); + if (binsh.canRead()) { + ps.println("#!"+binsh.getPath()); + } + ps.println("# This file generated by AspectJ installer"); + ps.println("# Created on "+new java.util.Date()+" by "+ + System.getProperty("user.name")); + ps.println(""); + ps.println("if [ \"$JAVA_HOME\" = \"\" ] ; then JAVA_HOME=" + + quote(true, false, context.javaPath.getAbsolutePath())); + ps.println("fi"); + ps.println("if [ \"$ASPECTJ_HOME\" = \"\" ] ; then ASPECTJ_HOME=" + + quote(true, false, context.getOutputDir())); + ps.println("fi"); + ps.println(""); + String sep = File.pathSeparator; + + ps.println("\"$JAVA_HOME/bin/java\" -classpath "+ + "\"$ASPECTJ_HOME/lib/aspectjtools.jar" + sep + + "$JAVA_HOME/lib/tools.jar" + sep + + "$CLASSPATH\""+ + " -Xmx64M " + className + + " " + makeScriptArgs(true)); + } + + + private void makeExecutable(File file) { + try { + Runtime curRuntime = Runtime.getRuntime(); + curRuntime.exec("chmod 777 " + quote(true, false, file)); + } catch (Throwable t) { + // ignore any errors that occur while trying to chmod + } + } + + + + private String makeScriptArgs(boolean unixStyle) { + if (unixStyle) { + return "\"$@\""; + } else if (context.onWindowsPro()) { + return "%*"; + } else { + return "%1 %2 %3 %4 %5 %6 %7 %8 %9"; + } + } + + private String quote(boolean unixStyle, boolean forceQuotes, File file) { + return quote(unixStyle, forceQuotes, file.getAbsolutePath()); + } + private String quote(boolean unixStyle, boolean forceQuotes, String s) { + if (context.onWindows() && unixStyle) { + s = s.replace('\\', '/'); + } + + if (!forceQuotes && s.indexOf(' ') == -1) return s; + return "\""+s+"\""; + } + + private File makeScriptFile(String name, boolean unixStyle) throws IOException { + if (!unixStyle) { + if (context.onOS2()) { + name += ".cmd"; + } else if (context.onWindows()) { + name += ".bat"; + } + } + + //XXX probably want a context.getOutputBinDir() + File bindir = new File(context.getOutputDir(), "bin"); + bindir.mkdirs(); + File file = new File(bindir, name); + return file; + } + + private PrintStream getPrintStream(File file) throws IOException { + return new PrintStream(new BufferedOutputStream(new FileOutputStream(file))); + } + + String makeClassPathVar(boolean unixStyle) { + if (unixStyle) { + return "$CLASSPATH"; + } else { + return "%CLASSPATH%"; + } + } + + public String makeClassPath(boolean unixStyle) throws IOException { + return context.toolsJarPath.getAbsolutePath() + File.pathSeparator + + //XXX want context.getOutputLibDir() + new File(new File(context.getOutputDir(), "lib"), "aspectjtools.jar").getAbsolutePath() + + File.pathSeparator + makeClassPathVar(unixStyle); + } + + public void writeScript(String className, PrintStream ps, boolean unixStyle) throws IOException { + if (unixStyle) writeUnixHeader(className, ps); + else writeWindowsHeader(className, ps); + + /* + ps.print(quote(unixStyle, false, context.javaPath.getAbsolutePath())); + ps.print(" "); + ps.print("-classpath "); + ps.print(quote(unixStyle, true, makeClassPath(unixStyle))); + ps.print(" "); + ps.print("-Xmx64M "); + ps.print(className); + ps.print(" "); + ps.print(makeScriptArgs(unixStyle)); + */ + } + + public void writeScript(String className, boolean unixStyle) throws IOException { + File file = makeScriptFile(className, unixStyle); + if (!checkExistingFile(file)) return; + PrintStream ps = getPrintStream(file); + writeScript(toolsPackage + '.' + className + ".Main", ps, unixStyle); + ps.close(); + //??? unixStyle vs. onUnix() + if (context.onUnix()) makeExecutable(file); + } + + public boolean checkExistingFile(File file) { + if (!file.exists()) return true; + + return context.shouldOverwrite(file); + } + + /* + final static String OVERWRITE_MESSAGE = "Overwrite launch script "; + final static String OVERWRITE_TITLE = "Overwrite?"; + + final static String[] OVERWRITE_OPTIONS = { + "Yes", "No", "Yes to all", "No to all" + }; + + final static int OVERWRITE_YES = 0; + final static int OVERWRITE_NO = 1; + final static int OVERWRITE_ALL = 2; + final static int OVERWRITE_NONE = 3; + + int overwriteState = OVERWRITE_NO; + boolean shouldOverwrite(final File file) { + if (overwriteState == OVERWRITE_ALL) return true; + if (overwriteState == OVERWRITE_NONE) return false; + + try { + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + int ret = JOptionPane.showOptionDialog(context.installer.frame, + OVERWRITE_MESSAGE+file.getPath(), OVERWRITE_TITLE, + JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null, + OVERWRITE_OPTIONS, OVERWRITE_OPTIONS[OVERWRITE_YES]); + + overwriteState = ret; + } + }); + } catch (InvocationTargetException ite) { + context.handleException(ite.getTargetException()); + } catch (InterruptedException ie) { + } + + return overwriteState == OVERWRITE_YES || overwriteState == OVERWRITE_ALL; + } + */ + + + public void writeScript(String className) throws IOException { + writeScript(className, true); + if (context.onWindows()) { + writeScript(className, false); + } + } +} + + +class JarUnpacker { + InstallContext context; + InstallPane installPane; + + public JarUnpacker(InstallContext context, InstallPane installPane) { + this.context = context; + this.installPane = installPane; + } + + public File makeOutputFile(String name, File outputFile) { + int index; + int lastIndex = 0; + + while ( (index = name.indexOf('/', lastIndex)) != -1) { + outputFile = new File(outputFile, name.substring(lastIndex, index)); + lastIndex = index + 1; + } + + return new File(outputFile, name.substring(lastIndex)); + } + + final static int BUF_SIZE = 4096; + + public void writeStream(ZipInputStream zis, File outputFile) throws IOException { + if (outputFile.exists()) { + if (!context.shouldOverwrite(outputFile)) return; + } + + installPane.progressMessage("writing " + outputFile.getAbsolutePath()); + + outputFile.getParentFile().mkdirs(); + + if (context.isTextFile(outputFile)) { + writeTextStream(zis, outputFile); + } else { + writeBinaryStream(zis, outputFile); + } + } + + public void writeBinaryStream(ZipInputStream zis, File outputFile) throws IOException { + byte[] buffer = new byte[BUF_SIZE]; + int nRead = 0; + + OutputStream os = new BufferedOutputStream(new FileOutputStream(outputFile)); + + while ( (nRead = zis.read(buffer)) != -1) { + os.write(buffer, 0, nRead); + installPane.progressBytesWritten(nRead); + } + os.close(); + } + + public void writeTextStream(ZipInputStream zis, File outputFile) throws IOException { + BufferedWriter os = new BufferedWriter(new FileWriter(outputFile)); + BufferedReader r = new BufferedReader(new InputStreamReader(zis, "US-ASCII")); + + String l; + while ( (l = r.readLine()) != null) { + os.write(l); + os.newLine(); + installPane.progressBytesWritten(l.length() + 1); + } + os.close(); + } + + public void writeEntry(ZipInputStream zis, ZipEntry entry, File outputDir) throws IOException { + if (entry.isDirectory()) return; + + String name = entry.getName(); + File outputFile = makeOutputFile(name, outputDir); + writeStream(zis, outputFile); + } + + public void unpack(String jarName, File outputDir) throws IOException { + URL url = getClass().getResource(jarName); + InputStream stream = url.openStream(); + ZipInputStream zis = new ZipInputStream(stream); + int i = 0; + + ZipEntry entry; + while ((entry = zis.getNextEntry()) != null) { + final String name = entry.getName(); + writeEntry(zis, entry, outputDir); + // + } + installPane.progressMessage("done writing"); + } +} + diff --git a/build/src/org/aspectj/internal/tools/ant/taskdefs/AJInstaller.java b/build/src/org/aspectj/internal/tools/ant/taskdefs/AJInstaller.java new file mode 100644 index 000000000..4dd35c4b8 --- /dev/null +++ b/build/src/org/aspectj/internal/tools/ant/taskdefs/AJInstaller.java @@ -0,0 +1,330 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + +//XXX INCLUDES CODE FROM ANT -- UNDER APACHE LICENSE +package org.aspectj.internal.tools.ant.taskdefs; + +import org.apache.tools.ant.types.*; +import java.io.*; +import java.util.zip.ZipOutputStream; +import java.util.*; +import java.util.zip.*; +import org.apache.tools.ant.taskdefs.*; +import org.apache.tools.ant.*; + +public class AJInstaller extends MatchingTask { + static final String INCLUDE_CLASSES = "$installer$/org/aspectj/*.class"; + static final String MAIN_CLASS = "$installer$.org.aspectj.Main"; + static final String CONTENTS_FILE = "$installer$/org/aspectj/resources/contents.txt"; + private String htmlSrc; + + public void setHtmlSrc(String v) { htmlSrc = v; } + + private String resourcesSrc; + + public void setResourcesSrc(String v) { resourcesSrc = v; } + + private String mainclass; + + public void setMainclass(String v) { mainclass = v; } + + private File installerClassJar; + + public void setInstallerclassjar(String v) { + installerClassJar = project.resolveFile(v); + } + + protected List contentsNames = new ArrayList(); + + protected long contentsBytes = 0; + + protected void addToContents(File file, String vPath) { + contentsNames.add(vPath); + contentsBytes += file.length(); + } + + String[] getFiles(File baseDir) { + DirectoryScanner ds = new DirectoryScanner(); + setBasedir(baseDir.getAbsolutePath()); + ds.setBasedir(baseDir); + //ds.setIncludes(new String [] {pattern}); + ds.scan(); + return ds.getIncludedFiles(); + } + + protected Copy getCopyTask() { + Copy cd = (Copy)project.createTask("copy"); + if (null == cd) { + log("project.createTask(\"copy\") failed - direct", project.MSG_VERBOSE); + cd = new Copy(); + cd.setProject(getProject()); + } + return cd; + } + protected void finishZipOutputStream(ZipOutputStream zOut) throws IOException, BuildException { + writeContents(zOut); + writeManifest(zOut); + File tmpDirF = File.createTempFile("tgz", ".di"); + File tmpDir = new File(tmpDirF.getAbsolutePath() + "r"); + tmpDirF.delete(); + String tmp = tmpDir.getAbsolutePath(); + + // installer class files + Expand expand = new Expand(); + expand.setProject(getProject()); + expand.setSrc(installerClassJar); + expand.setDest(new File(tmp)); + PatternSet patterns = new PatternSet(); + patterns.setIncludes(INCLUDE_CLASSES); + expand.addPatternset(patterns); + expand.execute(); + + // move the correct resource files into the jar + Copy cd = getCopyTask(); + fileset = new FileSet(); + fileset.setDir(new File(resourcesSrc)); + fileset.setIncludes("*"); + fileset.setExcludes("contents.txt,properties.txt"); + cd.addFileset(fileset); + cd.setTodir(new File(tmp+"/$installer$/org/aspectj/resources")); + cd.execute(); + project.addFilter("installer.main.class", this.mainclass); + Copy cf = getCopyTask(); + fileset = new FileSet(); + fileset.setDir(new File(resourcesSrc)); + fileset.setIncludes("properties.txt"); + cf.setFiltering(true); + cf.addFileset(fileset); + cf.setTodir(new File(tmp+"/$installer$/org/aspectj/resources")); + cf.execute(); + // move the correct resource files into the jar + cd = getCopyTask(); + fileset = new FileSet(); + fileset.setDir(new File(htmlSrc)); + fileset.setIncludes("*"); + cd.addFileset(fileset); + cd.setTodir(new File(tmp+"/$installer$/org/aspectj/resources")); + cd.execute(); + // now move these files into the jar + setBasedir(tmp); + writeFiles(zOut, getFiles(tmpDir)); + // and delete the tmp dir + Delete dt = (Delete)project.createTask("delete"); + if (null == dt) { + dt = new Delete(); + dt.setProject(getProject()); + } + dt.setDir(new File(tmp)); + dt.execute(); + } + + static final char NEWLINE = '\n'; + + protected void writeContents(ZipOutputStream zOut) throws IOException { + // write to a StringBuffer + StringBuffer buf = new StringBuffer(); + buf.append(contentsBytes); + buf.append(NEWLINE); + for (Iterator i = contentsNames.iterator(); i.hasNext(); ) { + String name = (String)i.next(); + buf.append(name); + buf.append(NEWLINE); + } + zipFile(new StringBufferInputStream(buf.toString()), zOut, CONTENTS_FILE, System.currentTimeMillis()); + } + + protected void writeManifest(ZipOutputStream zOut) throws IOException { + // write to a StringBuffer + StringBuffer buf = new StringBuffer(); + buf.append("Manifest-Version: 1.0"); + buf.append(NEWLINE); + buf.append("Main-Class: " + MAIN_CLASS); + buf.append(NEWLINE); + zipFile(new StringBufferInputStream(buf.toString()), zOut, "META-INF/MANIFEST.MF", System.currentTimeMillis()); + } + + //XXX cut-and-paste from Zip super-class (under apache license) + private File zipFile; + private File baseDir; + private boolean doCompress = true; + protected String archiveType = "zip"; + + /** + * This is the name/location of where to + * create the .zip file. + */ + public void setZipfile(String zipFilename) { + zipFile = project.resolveFile(zipFilename); + } + + /** + * This is the base directory to look in for + * things to zip. + */ + public void setBasedir(String baseDirname) { + baseDir = project.resolveFile(baseDirname); + } + + /** + * Sets whether we want to compress the files or only store them. + */ + public void setCompress(String compress) { + doCompress = Project.toBoolean(compress); + } + + protected void initZipOutputStream(ZipOutputStream zOut) + throws IOException, BuildException + { + } + + protected void zipDir(File dir, ZipOutputStream zOut, String vPath) + throws IOException + { + } + + protected void zipFile(InputStream in, ZipOutputStream zOut, String vPath, + long lastModified) + throws IOException + { + ZipEntry ze = new ZipEntry(vPath); + ze.setTime(lastModified); + + /* + * XXX ZipOutputStream.putEntry expects the ZipEntry to know its + * size and the CRC sum before you start writing the data when using + * STORED mode. + * + * This forces us to process the data twice. + * + * I couldn't find any documentation on this, just found out by try + * and error. + */ + if (!doCompress) { + long size = 0; + CRC32 cal = new CRC32(); + if (!in.markSupported()) { + // Store data into a byte[] + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + byte[] buffer = new byte[8 * 1024]; + int count = 0; + do { + size += count; + cal.update(buffer, 0, count); + bos.write(buffer, 0, count); + count = in.read(buffer, 0, buffer.length); + } while (count != -1); + in = new ByteArrayInputStream(bos.toByteArray()); + } else { + in.mark(Integer.MAX_VALUE); + byte[] buffer = new byte[8 * 1024]; + int count = 0; + do { + size += count; + cal.update(buffer, 0, count); + count = in.read(buffer, 0, buffer.length); + } while (count != -1); + in.reset(); + } + ze.setSize(size); + ze.setCrc(cal.getValue()); + } + zOut.putNextEntry(ze); + byte[] buffer = new byte[8 * 1024]; + int count = 0; + do { + zOut.write(buffer, 0, count); + count = in.read(buffer, 0, buffer.length); + } while (count != -1); + } + + protected void zipFile(File file, ZipOutputStream zOut, String vPath) + throws IOException + { + if ( !vPath.startsWith("$installer$") ) { + addToContents(file, vPath); + } + FileInputStream fIn = new FileInputStream(file); + try { + zipFile(fIn, zOut, vPath, file.lastModified()); + } finally { + fIn.close(); + } + } + + public void execute() throws BuildException { + if (installerClassJar == null) { + throw new BuildException("installerClassJar attribute must be set!"); + } + if (!installerClassJar.canRead() + || !installerClassJar.getPath().endsWith(".jar")) { + throw new BuildException("not readable jar:" + installerClassJar); + } +// if (installerClassDir == null) { +// throw new BuildException("installerClassDir attribute must be set!"); +// } +// if (!installerClassDir.exists()) { +// throw new BuildException("no such directory: installerClassDir=" + installerClassDir); +// } + if (baseDir == null) { + throw new BuildException("basedir attribute must be set!"); + } + if (!baseDir.exists()) { + throw new BuildException("basedir does not exist!"); + } + DirectoryScanner ds = super.getDirectoryScanner(baseDir); + String[] files = ds.getIncludedFiles(); + String[] dirs = ds.getIncludedDirectories(); + log("Building installer: "+ zipFile.getAbsolutePath()); + ZipOutputStream zOut = null; + try { + zOut = new ZipOutputStream(new FileOutputStream(zipFile)); + if (doCompress) { + zOut.setMethod(ZipOutputStream.DEFLATED); + } else { + zOut.setMethod(ZipOutputStream.STORED); + } + initZipOutputStream(zOut); + writeDirs(zOut, dirs); + writeFiles(zOut, files); + finishZipOutputStream(zOut); + } catch (IOException ioe) { + String msg = "Problem creating " + archiveType + " " + ioe.getMessage(); + throw new BuildException(msg, ioe, location); + } finally { + if (zOut != null) { + try { + // close up + zOut.close(); + } + catch (IOException e) {} + } + } + } + + protected void writeDirs(ZipOutputStream zOut, String[] dirs) throws IOException { + for (int i = 0; i < dirs.length; i++) { + File f = new File(baseDir,dirs[i]); + String name = dirs[i].replace(File.separatorChar,'/')+"/"; + zipDir(f, zOut, name); + } + } + + protected void writeFiles(ZipOutputStream zOut, String[] files) throws IOException { + for (int i = 0; i < files.length; i++) { + File f = new File(baseDir,files[i]); + String name = files[i].replace(File.separatorChar,'/'); + zipFile(f, zOut, name); + } + } + +} diff --git a/build/src/org/aspectj/internal/tools/ant/taskdefs/AJPush.java b/build/src/org/aspectj/internal/tools/ant/taskdefs/AJPush.java new file mode 100644 index 000000000..4a778cb67 --- /dev/null +++ b/build/src/org/aspectj/internal/tools/ant/taskdefs/AJPush.java @@ -0,0 +1,89 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + +package org.aspectj.internal.tools.ant.taskdefs; + +import org.apache.tools.ant.*; +import org.apache.tools.ant.types.*; +import org.apache.tools.ant.taskdefs.*; +import java.io.*; +import java.util.*; +import java.text.DecimalFormat; + +public class AJPush extends ConditionalTask { + private File src; + + public void setSrc(String v) { src = project.resolveFile(v); } + + private String key; + + public void setKey(String v) { key = v; } + + File releaseDir = null; + File downloadDir = null; + boolean waiting = false; + + public void execute() throws org.apache.tools.ant.BuildException { + //File releaseDir = src.getParentFile(); + // todo: dependency on ant script variable name aj.release.dir + releaseDir = project.resolveFile(project.getProperty("aj.release.dir")); + // todo: dependency on ant script variable name download.dir + downloadDir = project.resolveFile(project.getProperty("download.dir")); + // For testing make sure these directories are made + Mkdir mkdir = (Mkdir) project.createTask("mkdir"); + mkdir.setDir(releaseDir); + mkdir.execute(); + mkdir = (Mkdir) project.createTask("mkdir"); + mkdir.setDir(downloadDir); + mkdir.execute(); + log("Pushing from " + releaseDir + " to " + downloadDir); + // add info to release.txt + try { + File releaseFile = new File(releaseDir, "release.txt"); + File downloadFile = new File(downloadDir, "release.txt"); + if (!releaseFile.canRead()) { + releaseFile.createNewFile(); + } + addReleaseInfo(src, releaseFile); + // copy to staging web server + project.copyFile(src, new File(downloadDir, src.getName())); + project.copyFile(releaseFile, downloadFile); + } catch (IOException ioe) { + ioe.printStackTrace(); + } + } + + void addReleaseInfo(File file, File propFile) throws IOException { + Properties props = new Properties(); + if (propFile.canRead()) { + props.load(new FileInputStream(propFile)); + } + file.createNewFile(); // create new only if necessary + long bytes = file.length(); + DecimalFormat df = new DecimalFormat(); + df.setGroupingSize(3); + String bytesString = df.format(bytes); + props.put("release." + key + ".size.bytes", bytesString); + props.put("release." + key + ".date", project.getProperty("build.date")); + props.put("release." + key + ".filename", file.getName()); + props.put("release.date", project.getProperty("build.date")); + props.put("release.version", project.getProperty("build.version.short")); + props.put("release.versionName", project.getProperty("build.version.long")); + String userName = System.getProperty("user.name"); + if (userName != null) { + props.put("release." + key + ".username", userName); + } + props.store(new FileOutputStream(propFile), null); + } + +} diff --git a/build/src/org/aspectj/internal/tools/ant/taskdefs/AntBuilder.java b/build/src/org/aspectj/internal/tools/ant/taskdefs/AntBuilder.java new file mode 100644 index 000000000..d4001161e --- /dev/null +++ b/build/src/org/aspectj/internal/tools/ant/taskdefs/AntBuilder.java @@ -0,0 +1,721 @@ +/* ******************************************************************* + * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ +package org.aspectj.internal.tools.ant.taskdefs; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.Target; +import org.apache.tools.ant.Task; +import org.apache.tools.ant.taskdefs.Copy; +import org.apache.tools.ant.taskdefs.Filter; +import org.apache.tools.ant.taskdefs.Javac; +import org.apache.tools.ant.taskdefs.Zip; +import org.apache.tools.ant.types.FileSet; +import org.apache.tools.ant.types.Path; +import org.apache.tools.ant.types.ZipFileSet; +import org.aspectj.internal.tools.build.BuildSpec; +import org.aspectj.internal.tools.build.Builder; +import org.aspectj.internal.tools.build.Messager; +import org.aspectj.internal.tools.build.Module; +import org.aspectj.internal.tools.build.Modules; +import org.aspectj.internal.tools.build.ProductModule; +import org.aspectj.internal.tools.build.Util; + +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.List; +import java.util.Properties; + +/** + * Implement Builder in Ant. + */ +public class AntBuilder extends Builder { + /* + * XXX This just constructs and uses Ant Task objects, + * which in some cases causes the tasks to fail. + */ + + /** @return a Builder for this project and configuration */ + public static Builder getBuilder(String config, Project project, File tempDir) { + boolean useEclipseCompiles = false; + boolean verbose = false; + if (null != config) { + if (-1 != config.indexOf("useEclipseCompiles")) { + useEclipseCompiles = true; + } + if (-1 != config.indexOf("verbose")) { + verbose = true; + } + } + Messager handler = new ProjectMessager(project); + Builder result = new ProductBuilder(project, tempDir, useEclipseCompiles, handler); + if (verbose) { + result.setVerbose(true); + } + return result; + } + + /** + * Make and register target for this module and antecedants. + * This ensures that the (direct) depends list is generated + * for each target. + * This depends on topoSort to detect cycles. XXX unverified + */ + private static void makeTargetsForModule( + final Module module, + final Hashtable targets, + final boolean rebuild) { + Target target = (Target) targets.get(module.name); + if (null == target) { + // first add the target + target = new Target(); + target.setName(module.name); + List req = module.getRequired(); + StringBuffer depends = new StringBuffer(); + boolean first = true; + for (Iterator iterator = req.iterator(); iterator.hasNext();) { + Module reqModule = (Module) iterator.next(); + if (rebuild || reqModule.outOfDate(false)) { + if (!first) { + depends.append(","); + } else { + first = false; + } + depends.append(reqModule.name); + } + } + if (0 < depends.length()) { + target.setDepends(depends.toString()); + } + targets.put(module.name, target); + + // then recursively add any required modules + for (Iterator iterator = module.getRequired().iterator(); + iterator.hasNext(); + ) { + Module reqModule = (Module) iterator.next(); + if (rebuild || reqModule.outOfDate(false)) { + makeTargetsForModule(reqModule, targets, rebuild); + } + } + } + } + + protected final Project project; + + protected AntBuilder(Project project, File tempDir, boolean useEclipseCompiles, + Messager handler) { + super(tempDir, useEclipseCompiles, handler); + this.project = project; + Util.iaxIfNull(project, "project"); + } + protected boolean copyFile(File fromFile, File toFile, boolean filter) { + Copy copy = makeCopyTask(filter); + copy.setFile(fromFile); + copy.setTofile(toFile); + executeTask(copy); + return true; + } + + protected void copyFileset(File toDir, FileSet fileSet, boolean filter) { + Copy copy = makeCopyTask(filter); + copy.addFileset(fileSet); + copy.setTodir(toDir); + executeTask(copy); + } + + /** filter if FILTER_ON, use filters */ + protected Copy makeCopyTask(boolean filter) { + Copy copy = new Copy(); + copy.setProject(project); + if (FILTER_ON == filter) { + copy.setFiltering(true); + setupFilters(); + } + return copy; + } + + /** lazily and manually generate properties */ + protected Properties getFilterProperties() { // + if (filterProps == null) { + long time = System.currentTimeMillis(); + String version = null; // unknown - system? + filterProps = BuildSpec.getFilterProperties(time, version); + } + return filterProps; + } + + protected void setupFilters() { + if (!filterSetup) { // XXX check Ant - is this static? + Properties props = getFilterProperties(); + for (Enumeration enum = props.keys(); enum.hasMoreElements();) { + String token = (String) enum.nextElement(); + String value = props.getProperty(token); + Filter filter = new Filter(); + filter.setProject(project); + filter.setToken(token); + filter.setValue(value); + if (!executeTask(filter)) { + return; + } + } + filterSetup = true; + } + } + + protected boolean compile(Module module, File classesDir, List errors) { + // XXX test whether build.compiler property takes effect automatically + // I suspect it requires the proper adapter setup. + Javac javac = new Javac(); + javac.setProject(project); + + // -- source paths + Path path = new Path(project); + for (Iterator iter = module.getSrcDirs().iterator(); iter.hasNext();) { + File file = (File) iter.next(); + path.createPathElement().setLocation(file); + } + javac.setSrcdir(path); + path = null; + + // -- classpath + Path classpath = new Path(project); + boolean hasLibraries = false; + // modules + for (Iterator iter = module.getRequired().iterator(); iter.hasNext();) { + Module required = (Module) iter.next(); + classpath.createPathElement().setLocation(required.getModuleJar()); + if (!hasLibraries) { + hasLibraries = true; + } + } + // libraries + for (Iterator iter = module.getLibJars().iterator(); iter.hasNext();) { + File file = (File) iter.next(); + classpath.createPathElement().setLocation(file); + if (!hasLibraries) { + hasLibraries = true; + } + } + // need to add system classes?? + boolean inEclipse = true; // XXX detect, fork only in eclipse + if (hasLibraries && inEclipse) { + javac.setFork(true); // XXX otherwise never releases library jars + } + + // -- set output directory + classpath.createPathElement().setLocation(classesDir); + javac.setClasspath(classpath); + javac.setDestdir(classesDir); + if (!classesDir.mkdirs()) { + errors.add("unable to create classes directory"); + return false; + } + + // compile + try { + return executeTask(javac); + } catch (BuildException e) { + String args = "" + Arrays.asList(javac.getCurrentCompilerArgs()); + errors.add("BuildException compiling " + module.toLongString() + args + + ": " + Util.renderException(e)); + return false; + } finally { + javac.init(); // be nice to let go of classpath libraries... + } + } + + /** + * Merge classes directory and any merge jars into module jar + * with any specified manifest file. + * META-INF directories are excluded. + */ + protected boolean assemble(Module module, File classesDir, List errors) { + if (!buildingEnabled) { + return false; + } + // ---- zip result up + Zip zip = new Zip(); + zip.setProject(project); + zip.setDestFile(module.getModuleJar()); + ZipFileSet zipfileset = null; + + // -- merge any resources in any of the src directories + for (Iterator iter = module.getSrcDirs().iterator(); iter.hasNext();) { + File srcDir = (File) iter.next(); + zipfileset = new ZipFileSet(); + zipfileset.setProject(project); + zipfileset.setDir(srcDir); + zipfileset.setIncludes(RESOURCE_PATTERN); + zip.addZipfileset(zipfileset); + } + + // -- merge any merge jars + List mergeJars = module.getMerges(); + final boolean useManifest = false; + if (0 < mergeJars.size()) { + for (Iterator iter = mergeJars.iterator(); iter.hasNext();) { + File mergeJar = (File) iter.next(); + zipfileset = new ZipFileSet(); + zipfileset.setProject(project); + zipfileset.setSrc(mergeJar); + zipfileset.setIncludes("**/*"); + zipfileset.setExcludes("META-INF/*"); // XXXFileLiteral + zipfileset.setExcludes("meta-inf/*"); + zip.addZipfileset(zipfileset); + } + } + // merge classes; put any meta-inf/manifest.mf here + File metaInfDir = new File(classesDir, "META-INF"); + if (metaInfDir.canWrite()) { + Util.deleteContents(metaInfDir); // XXX only delete manifest + } + + // -- manifest + File manifest = new File(module.moduleDir, module.name + ".mf.txt"); // XXXFileLiteral + if (Util.canReadFile(manifest)) { + if (Util.canReadDir(metaInfDir) || metaInfDir.mkdirs()) { + copyFile(manifest, new File(metaInfDir, "manifest.mf"), FILTER_ON); // XXXFileLiteral + } else { + errors.add("have manifest, but unable to create " + metaInfDir); + return false; + } + } + + zipfileset = new ZipFileSet(); + zipfileset.setProject(project); + zipfileset.setDir(classesDir); + zipfileset.setIncludes("**/*"); + zip.addZipfileset(zipfileset); + + try { + handler.log("assembling " + module + " in " + module.getModuleJar()); + return executeTask(zip); + } catch (BuildException e) { + errors.add("BuildException zipping " + module + ": " + e.getMessage()); + return false; + } finally { + module.clearOutOfDate(); + } + } + /** + * @see org.aspectj.internal.tools.build.Builder#buildAntecedants(Module) + */ + protected String[] getAntecedantModuleNames(Module module, boolean rebuild) { + Hashtable targets = new Hashtable(); + makeTargetsForModule(module, targets, rebuild); + // XXX bug: doc says topoSort returns String, but returns Target + Collection result = project.topoSort(module.name, targets); + // XXX is it topoSort that should detect cycles? + int size = result.size(); + if (0 == result.size()) { + return new String[0]; + } + ArrayList toReturn = new ArrayList(); + for (Iterator iter = result.iterator(); iter.hasNext();) { + Target target = (Target) iter.next(); + String name = target.getName(); + if (null == name) { + throw new Error("null name?"); + } else { + toReturn.add(name); + } + } + // topoSort always returns module.name + if ((1 == size) + && module.name.equals(toReturn.get(0)) + && !module.outOfDate(false)) { + return new String[0]; + } + return (String[]) toReturn.toArray(new String[0]); + } + + /** + * Generate Module.assembledJar with merge of itself and all antecedants + */ + protected boolean assembleAll(Module module, Messager handler) { + if (!buildingEnabled) { + return false; + } + Util.iaxIfNull(module, "module"); + Util.iaxIfNull(handler, "handler"); + if (module.outOfDate(false)) { + throw new IllegalStateException("module out of date: " + module); + } + + // ---- zip result up + Zip zip = new Zip(); + zip.setProject(project); + zip.setDestFile(module.getAssembledJar()); + ZipFileSet zipfileset = null; + + ArrayList known = module.findKnownJarAntecedants(); + + // -- merge any antecedents, less any manifest + for (Iterator iter = known.iterator(); iter.hasNext();) { + File jarFile = (File) iter.next(); + zipfileset = new ZipFileSet(); + zipfileset.setProject(project); + zipfileset.setSrc(jarFile); + zipfileset.setIncludes("**/*"); + zipfileset.setExcludes("META-INF/MANIFEST.MF"); // XXXFileLiteral + zipfileset.setExcludes("META-INF/manifest.mf"); + zipfileset.setExcludes("meta-inf/manifest.mf"); + zipfileset.setExcludes("meta-inf/MANIFEST.MF"); + zip.addZipfileset(zipfileset); + } + + // merge the module jar itself, including same manifest (?) + zipfileset = new ZipFileSet(); + zipfileset.setProject(project); + zipfileset.setSrc(module.getModuleJar()); + zip.addZipfileset(zipfileset); + + try { + handler.log("assembling all " + module + " in " + module.getAssembledJar()); + return executeTask(zip); + } catch (BuildException e) { + handler.logException("BuildException zipping " + module, e); + return false; + } finally { + module.clearOutOfDate(); + } + } + + /** + * @see org.aspectj.internal.tools.ant.taskdefs.Builder#buildInstaller(BuildSpec, String) + */ + protected boolean buildInstaller( + BuildSpec buildSpec, + String targDirPath) { + return false; + } + + /** + * @see org.aspectj.internal.tools.ant.taskdefs.Builder#copyFiles(File, File, String, String, boolean) + */ + protected boolean copyFiles( + File fromDir, + File toDir, + String includes, + String excludes, + boolean filter) { + Copy copy = makeCopyTask(filter); + copy.setTodir(toDir); + FileSet fileset = new FileSet(); + fileset.setDir(fromDir); + if (null != includes) { + fileset.setIncludes(includes); + } + if (null != excludes) { + fileset.setExcludes(excludes); + } + copy.addFileset(fileset); + executeTask(copy); + + return false; + } + + /** task.execute() and any advice */ + protected boolean executeTask(Task task) { + if (!buildingEnabled) { + return false; + } + task.execute(); + return true; + } + +} + + +// finally caught by failing to comply with proper ant initialization +// /** +// * Build a module that has a build script. +// * @param buildSpec the module to build +// * @param buildScript the script file +// * @throws BuildException if build fails +// */ +// private void buildByScript(BuildSpec buildSpec, File buildScript) +// throws BuildException { +// Ant ant = new Ant(); +// ant.setProject(getProject()); +// ant.setAntfile(buildScript.getAbsolutePath()); +// ant.setDescription("building module " + buildSpec.module); +// ant.setDir(buildScript.getParentFile()); +// ant.setInheritAll(true); +// ant.setInheritRefs(false); +// ant.setLocation(getLocation()); +// ant.setOwningTarget(getOwningTarget()); +// // by convention, for build.xml, use module name to publish +// ant.setTarget(buildSpec.module); +// ant.setTaskName("ant"); +// loadAntProperties(ant, buildSpec); +// ant.execute(); +// } +// +// /** override definitions */ +// private void loadAntProperties(Ant ant, BuildSpec buildSpec) { +// Property property = ant.createProperty(); +// property.setName(BuildSpec.baseDir_NAME); +// property.setFile(buildSpec.baseDir); +// property = ant.createProperty(); +// property.setName(buildSpec.distDir_NAME); +// property.setFile(buildSpec.distDir); +// property = ant.createProperty(); +// property.setName(BuildSpec.tempDir_NAME); +// property.setFile(buildSpec.tempDir); +// property = ant.createProperty(); +// property.setName(BuildSpec.jarDir_NAME); +// property.setFile(buildSpec.jarDir); +// property = ant.createProperty(); +// property.setName(BuildSpec.stagingDir_NAME); +// property.setFile(buildSpec.stagingDir); +// } + + +/** + * Segregate product-building API's from module-building APIs for clarity. + * These are called by the superclass if the BuildSpec warrants. + * XXX extremely brittle/arbitrary assumptions. + * @see BuildModule for assumptions + */ +class ProductBuilder extends AntBuilder { + + private static String getProductInstallResourcesSrc(BuildSpec buildSpec) { + final String resourcesName = "installer-resources"; // XXXFileLiteral + File dir = buildSpec.productDir.getParentFile(); + String result = null; + if (null == dir) { + return "../../" + resourcesName; + } + dir = dir.getParentFile(); + if (null == dir) { + return "../" + resourcesName; + } else { + dir = new File(dir, resourcesName); + return dir.getPath(); + } + } + + private static String getProductInstallerFileName(BuildSpec buildSpec) { // XXXFileLiteral + return "aspectj-" + + buildSpec.productDir.getName() + + "-" + + Util.shortVersion(buildSpec.version) + + ".jar"; + } + + /** + * Calculate name of main, typically InitialCap, and hence installer class. + * @return $$installer$$.org.aspectj." + ProductName + "Installer" + */ + + private static String getProductInstallerMainClass(BuildSpec buildSpec) { + String productName = buildSpec.productDir.getName(); + String initial = productName.substring(0, 1).toUpperCase(); + productName = initial + productName.substring(1); + return "$installer$.org.aspectj." + productName + "Installer"; // XXXNameLiteral + } + + /** @see Builder.getBuilder(String, Project, File) */ + ProductBuilder( + Project project, + File tempDir, + boolean useEclipseCompiles, + Messager handler) { + super(project, tempDir, useEclipseCompiles, handler); + } + + /** + * Build product by discovering any modules to build, + * building those, assembling the product distribution, + * and optionally creating an installer for it. + * @return true on success + */ + protected boolean buildProduct(BuildSpec buildSpec) + throws BuildException { + Util.iaxIfNull(buildSpec, "buildSpec"); + // XXX if installer and not out of date, do not rebuild unless rebuild set + + if (!buildSpec.trimTesting) { + buildSpec.trimTesting = true; + handler.log("testing trimmed for " + buildSpec); + } + Util.iaxIfNotCanReadDir(buildSpec.productDir, "productDir"); + Util.iaxIfNotCanReadDir(buildSpec.baseDir, "baseDir"); + Util.iaxIfNotCanWriteDir(buildSpec.distDir, "distDir"); + + // ---- discover modules to build, and build them + Modules modules = new Modules( + buildSpec.baseDir, + buildSpec.jarDir, + buildSpec.trimTesting, + handler); + ProductModule[] productModules = discoverModules(buildSpec.productDir, modules); + for (int i = 0; i < productModules.length; i++) { + if (buildSpec.verbose) { + handler.log("building product module " + productModules[i]); + } + if (!buildProductModule(productModules[i])) { + return false; + } + } + if (buildSpec.verbose) { + handler.log("assembling product module for " + buildSpec); + } + + // ---- assemble product distribution + final String productName = buildSpec.productDir.getName(); + final File targDir = new File(buildSpec.distDir, productName); + final String targDirPath = targDir.getPath(); + if (targDir.canWrite()) { + Util.deleteContents(targDir); + } + + if (!targDir.canWrite() && !targDir.mkdirs()) { + if (buildSpec.verbose) { + handler.log("unable to create " + targDir); + } + return false; + } + // filter-copy everything but the binaries + Copy copy = makeCopyTask(true); + copy.setTodir(targDir); + File distDir = new File(buildSpec.productDir, "dist"); // XXXFileLiteral + Util.iaxIfNotCanReadDir(distDir, "product dist directory"); + FileSet fileset = new FileSet(); + fileset.setDir(distDir); + fileset.setExcludes(Builder.BINARY_SOURCE_PATTERN); + copy.addFileset(fileset); + if (!executeTask(copy)) { + return false; + } + + // copy binaries (but not module flag files) + String excludes = null; + { + StringBuffer buf = new StringBuffer(); + for (int i = 0; i < productModules.length; i++) { + if (0 < buf.length()) { + buf.append(","); + } + buf.append(productModules[i].relativePath); + } + if (0 < buf.length()) { + excludes = buf.toString(); + } + } + copy = makeCopyTask(false); + copy.setTodir(targDir); + fileset = new FileSet(); + fileset.setDir(distDir); + fileset.setIncludes(Builder.BINARY_SOURCE_PATTERN); + if (null != excludes) { + fileset.setExcludes(excludes); + } + copy.addFileset(fileset); + if (!executeTask(copy)) { + return false; + } + + // copy binaries associated with module flag files + for (int i = 0; i < productModules.length; i++) { + ProductModule product = productModules[i]; + String targPath = targDirPath + "/" + product.relativePath; + File jarFile = (product.assembleAll + ? product.module.getAssembledJar() + : product.module.getModuleJar() ); + copyFile(jarFile, new File(targPath), FILTER_OFF); + } + handler.log("created product in " + targDir); + // ---- create installer + if (buildSpec.createInstaller) { + return buildInstaller(buildSpec, targDirPath); + } else { + return true; + } + } + + protected boolean buildInstaller(BuildSpec buildSpec, String targDirPath) { + if (buildSpec.verbose) { + handler.log("creating installer for " + buildSpec); + } + AJInstaller installer = new AJInstaller(); + installer.setProject(project); + installer.setBasedir(targDirPath); + //installer.setCompress(); + File installSrcDir = new File(buildSpec.productDir, "install"); // XXXFileLiteral + Util.iaxIfNotCanReadDir(installSrcDir, "installSrcDir"); + installer.setHtmlSrc(installSrcDir.getPath()); + String resourcePath = getProductInstallResourcesSrc(buildSpec); + File resourceSrcDir = new File(resourcePath); + Util.iaxIfNotCanReadDir(resourceSrcDir, "resourceSrcDir"); + installer.setResourcesSrc(resourcePath); + String name = getProductInstallerFileName(buildSpec); + File outFile = new File(buildSpec.jarDir, name); + installer.setZipfile(outFile.getPath()); + installer.setMainclass(getProductInstallerMainClass(buildSpec)); + installer.setInstallerclassjar(getBuildJar(buildSpec)); + return executeTask(installer); + + // -- test installer XXX + // create text setup file + // run installer with setup file + // cleanup installed product + } + + private String getBuildJar(BuildSpec buildSpec) { + return buildSpec.baseDir.getPath() + + "/lib/build/build.jar" ; // XXX + } + + private Module moduleForReplaceFile(File replaceFile, Modules modules) { + String jarName = moduleAliasFor(replaceFile.getName().toLowerCase()); + if (jarName.endsWith(".jar") || jarName.endsWith(".zip")) { // XXXFileLiteral + jarName = jarName.substring(0, jarName.length()-4); + } else { + throw new IllegalArgumentException("can only replace .[jar|zip]"); + } + boolean assembleAll = jarName.endsWith("-all"); + String name = (!assembleAll ? jarName : jarName.substring(0, jarName.length()-4)); + return modules.getModule(name); + } + +} + + +class ProjectMessager extends Messager { + private final Project project; + public ProjectMessager(Project project) { + Util.iaxIfNull(project, "project"); + this.project = project; + } + + public boolean log(String s) { + project.log(s); + return true; + } + public boolean error(String s) { + project.log(s, project.MSG_ERR); + return true; + } + public boolean logException(String context, Throwable thrown) { + project.log(context + Util.renderException(thrown), project.MSG_ERR); + return true; + } + +}
\ No newline at end of file diff --git a/build/src/org/aspectj/internal/tools/ant/taskdefs/BuildModule.java b/build/src/org/aspectj/internal/tools/ant/taskdefs/BuildModule.java new file mode 100644 index 000000000..a0f56f1d6 --- /dev/null +++ b/build/src/org/aspectj/internal/tools/ant/taskdefs/BuildModule.java @@ -0,0 +1,156 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.internal.tools.ant.taskdefs; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Location; +import org.apache.tools.ant.Task; +import org.apache.tools.ant.types.Path; +import org.aspectj.internal.tools.build.BuildSpec; +import org.aspectj.internal.tools.build.Builder; + +import java.io.File; + +/** + * Ant interface to build a product or module, including any required modules. + * @see Builder + */ +public class BuildModule extends Task { // quickie hack... + + public static void main(String[] args) { + TestBuildModule.main(args); + } + + private static File pathToFile(Path path) { + if (null != path) { + String[] list = path.list(); + if ((null == list) || (1 != list.length)) { + throw new IllegalArgumentException("expected exactly 1 element"); + } + return new File(list[0]); + } + return null; + } + BuildSpec buildSpec; + + public BuildModule() { + buildSpec = new BuildSpec(); + buildSpec.version = BuildSpec.BUILD_VERSION_DEFAULT; + } + + public void setModuledir(Path moduleDir) { + buildSpec.moduleDir = pathToFile(moduleDir); + } + + public void setModule(String module) { // XXX handle multiple modules, same builder + buildSpec.module = module; + } + + public void setVersion(String version) { + buildSpec.version = version; + } + public void setBasedir(Path baseDir) { + buildSpec.baseDir = pathToFile(baseDir); + } + + public void setJardir(Path jarDir) { + buildSpec.jarDir = pathToFile(jarDir); + } + + public void setTrimtesting(boolean trimTesting) { + buildSpec.trimTesting = trimTesting; + } + + public void setAssembleall(boolean assembleAll) { + buildSpec.assembleAll = assembleAll; + } + + public void setRebuild(boolean rebuild) { + buildSpec.rebuild = rebuild; + } + + public void setFailonerror(boolean failonerror) { + buildSpec.failonerror = failonerror; + } + + public void setCreateinstaller(boolean create) { + buildSpec.createInstaller = create; + } + + public void setVerbose(boolean verbose) { + buildSpec.verbose = verbose; + } + + public void setBuildConfig(String buildConfig) { + buildSpec.buildConfig = buildConfig; + } + + // --------------------------------------------------------- product build + + public void setProductdir(Path productDir) { + buildSpec.productDir = pathToFile(productDir); + } + + public void setTempdir(Path tempDir) { + buildSpec.tempDir = pathToFile(tempDir); + } + + public void setDistdir(Path distdir) { + buildSpec.distDir = pathToFile(distdir); + } + + public void execute() throws BuildException { + final BuildSpec buildSpec = this.buildSpec; + this.buildSpec = new BuildSpec(); + build(buildSpec); + } + + private void build(BuildSpec buildSpec) throws BuildException { + final boolean failonerror = buildSpec.failonerror; + Builder builder = null; + try { + File buildScript = new File(buildSpec.moduleDir, "build.xml"); // XXXFileLiteral + if (buildScript.canRead()) { + if (!buildByScript(buildSpec, buildScript)) { + log("unable to build " + buildSpec + " using script: " + buildScript); + } + } else { + builder = AntBuilder.getBuilder( + buildSpec.buildConfig, + getProject(), + buildSpec.tempDir); + if (!builder.build(buildSpec) && failonerror) { + Location loc = getLocation(); + throw new BuildException("error building " + buildSpec, loc); + } + } + } catch (BuildException e) { + throw e; + } catch (Throwable t) { + Location loc = getLocation(); + throw new BuildException("error building " + buildSpec, t, loc); + } finally { + if (null != builder) { + builder.cleanup(); + } + } + } + + boolean buildByScript(BuildSpec buildSpec, File buildScript) + throws BuildException { + return false; + } +} +
\ No newline at end of file diff --git a/build/src/org/aspectj/internal/tools/ant/taskdefs/Checklics.java b/build/src/org/aspectj/internal/tools/ant/taskdefs/Checklics.java new file mode 100644 index 000000000..d79c9774f --- /dev/null +++ b/build/src/org/aspectj/internal/tools/ant/taskdefs/Checklics.java @@ -0,0 +1,735 @@ +/* ******************************************************************* + * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + +package org.aspectj.internal.tools.ant.taskdefs; + +import org.apache.tools.ant.*; +import org.apache.tools.ant.types.*; +import org.apache.tools.ant.taskdefs.*; +import java.io.*; +import java.util.*; + +/** + * Check that included .java files contain license and copyright strings + * for MPL 1.0 (default), Apache, or CPL. + * Use list="true" to get a list of known license variants {license}-{copyrightHolder} + * todo reimplement with regexp and jdiff FileLine utilities + */ +public class Checklics extends MatchingTask { + /* + This does not enforce that copyrights are correct/current, + only that they exist. + E.g., the default behavior requires MPL but permits either + Xerox or PARC copyright holders and any valid year. + */ + public static final String MPL_TAG = "mpl"; + public static final String APACHE_TAG = "apache"; + public static final String CPL_IBM_PARC_TAG = "cpl-ibm|parc"; + public static final String CPL_IBM_TAG = "cpl-ibm"; + public static final String MPL_XEROX_PARC_TAG = "mpl-parc|xerox"; + public static final String MPL_ONLY_TAG = "mpl-only"; + public static final String MPL_PARC_TAG = "mpl-parc"; + public static final String PARC_COPYRIGHT_TAG = "parc-copy"; + public static final String CPL_IBM_PARC_XEROX_TAG = "cpl-ibm|parc|xerox"; + public static final String DEFAULT = CPL_IBM_PARC_XEROX_TAG; + + static final Hashtable LICENSES; // XXX unmodifiable Map + + static { + final String XEROX = "Xerox"; + final String PARC = "Palo Alto Research Center"; + final String APACHE = "The Apache Software Foundation"; + final String IBM = "IBM"; + final String IBM_LONG = "International Business Machines"; + final String LIC_APL = + "Apache Software Foundation (http://www.apache.org/)"; + // XXX + final String LIC_MPL = "http://aspectj.org/MPL/"; + //final String LIC_CPL = "http://www.eclipse.org/legal/cpl-v"; // XXX not versioned + final String LIC_CPL = "Common Public License"; + // XXX not versioned + License APL = new License(APACHE_TAG, LIC_APL, APACHE); + License MPL = new License(MPL_TAG, LIC_MPL, XEROX); + License MPL_XEROX_PARC = new License(DEFAULT, LIC_MPL, XEROX, PARC); + License CPL_IBM_PARC = new License(CPL_IBM_PARC_TAG,LIC_CPL, + new String[] { IBM_LONG, IBM, PARC }); + License CPL_IBM_PARC_XEROX = new License(CPL_IBM_PARC_XEROX_TAG,LIC_CPL, + new String[] { IBM_LONG, IBM, PARC, XEROX }); + License CPL_IBM = new License(CPL_IBM_TAG, LIC_CPL, IBM, IBM_LONG); + License MPL_ONLY = new License(MPL_ONLY_TAG, LIC_MPL); + License MPL_PARC = new License(MPL_PARC_TAG, LIC_MPL, PARC); + License PARC_COPYRIGHT = new License(PARC_COPYRIGHT_TAG, null, PARC); + LICENSES = new Hashtable(); + LICENSES.put(APL.tag, APL); + LICENSES.put(MPL.tag, MPL); + LICENSES.put(MPL_PARC.tag, MPL_PARC); + LICENSES.put(MPL_XEROX_PARC.tag, MPL_XEROX_PARC); + LICENSES.put(CPL_IBM_PARC.tag, CPL_IBM_PARC); + LICENSES.put(MPL_ONLY.tag, MPL_ONLY); + LICENSES.put(CPL_IBM.tag, CPL_IBM); + LICENSES.put(PARC_COPYRIGHT.tag, PARC_COPYRIGHT); + LICENSES.put(CPL_IBM_PARC_XEROX.tag, CPL_IBM_PARC_XEROX); + } + + /** @param args String[] { < sourcepath > {, < licenseTag > } } */ + public static void main(String[] args) { + switch (args.length) { + case 1 : runDirect(args[0], null); + break; + case 2 : runDirect(args[0], args[1]); + break; + default: + String options = "{replace-headers|get-years|list|{licenseTag}}"; + System.err.println("java {me} sourcepath " + options); + break; + } + } + + /** + * Run the license check directly + * @param sourcepaths String[] of paths to source directories + * @param license the String tag for the license, if any + * @throws IllegalArgumentException if sourcepaths is empty + * @return total number of failed licenses + */ + public static int runDirect(String sourcepath, String license) { + if ((null == sourcepath) || (1 > sourcepath.length())) { + throw new IllegalArgumentException("bad sourcepath: " + sourcepath); + } + Checklics me = new Checklics(); + Project p = new Project(); + p.setName("direct interface to Checklics"); + p.setBasedir("."); + me.setProject(p); + me.setSourcepath(new Path(p, sourcepath)); + if (null != license) { + if ("replace-headers".equals(license)) { + me.setReplaceheaders(true); + } else if ("get-years".equals(license)) { + me.setGetYears(true); + } else if ("list".equals(license)) { + me.setList(true); + } else { + me.setLicense(license); + } + } + me.execute(); + return me.failed; + } + + private Path sourcepath; + private License license; + private boolean list; + private String streamTag; + private boolean failOnError; + private boolean getYears; + private boolean replaceHeaders; + private int passed; + private int failed; + + private boolean printDirectories; + + /** @param list if true, don't run but list known license tags */ + public void setList(boolean list) { + this.list = list; + } + + public void setPrintDirectories(boolean print) { + printDirectories = print; + } + + /** + * When failOnError is true, if any file failed, throw BuildException + * listing number of files that file failed to pass license check + * @param fail if true, report errors by throwing BuildException + */ + public void setFailOnError(boolean fail) { + this.failOnError = fail; + } + + /** @param tl mpl | apache | cpl */ + public void setLicense(String tl) { + License input = (License) LICENSES.get(tl); + if (null == input) { + throw new BuildException("no license known for " + tl); + } + license = input; + } + + public void setSourcepath(Path path) { + if (sourcepath == null) { + sourcepath = path; + } else { + sourcepath.append(path); + } + } + + public Path createSourcepath() { + return sourcepath == null + ? (sourcepath = new Path(project)) + : sourcepath.createPath(); + } + + public void setSourcepathRef(Reference id) { + createSourcepath().setRefid(id); + } + + /** @param out "out" or "err" */ + public void setOutputStream(String out) { + this.streamTag = out; + } + + public void setReplaceheaders(boolean replaceHeaders) { + this.replaceHeaders = replaceHeaders; + } + + public void setGetYears(boolean getYears) { + this.getYears = getYears; + } + + /** list known licenses or check source tree */ + public void execute() throws BuildException { + if (list) { + list(); + } else if (replaceHeaders) { + replaceHeaders(); + } else if (getYears) { + getYears(); + } else { + checkLicenses(); + } + } + + private PrintStream getOut() { + return ("err".equals(streamTag) ? System.err : System.out); + } + + interface FileVisitor { + void visit(File file); + } + + /** visit all .java files in all directories... */ + private void visitAll(FileVisitor visitor) { + List filelist = new ArrayList(); + String[] dirs = sourcepath.list(); + for (int i = 0; i < dirs.length; i++) { + File dir = project.resolveFile(dirs[i]); + String[] files = getDirectoryScanner(dir).getIncludedFiles(); + for (int j = 0; j < files.length; j++) { + File file = new File(dir, files[j]); + String path = file.getPath(); + if (path.endsWith(".java")) { + visitor.visit(file); + } + } + } + } + + private void replaceHeaders() { + class YearVisitor implements FileVisitor { + public void visit(File file) { + HeaderInfo info = Header.checkFile(file); + if (!Header.replaceHeader(file, info)) { + throw new BuildException("failed to replace header for " + file + + " using " + info); + } + } + } + visitAll(new YearVisitor()); + } + + private void getYears() { + final PrintStream out = getOut(); + class YearVisitor implements FileVisitor { + public void visit(File file) { + HeaderInfo info = Header.checkFile(file); + out.println(info.toString()); + } + } + visitAll(new YearVisitor()); + } + + private void checkLicenses() throws BuildException { + if (null == license) { + setLicense(DEFAULT); + } + final License license = this.license; // being paranoid... + if (null == license) { + throw new BuildException("no license"); + } + final PrintStream out = getOut(); + + class Visitor implements FileVisitor { + int failed = 0; + int passed = 0; + public void visit(File file) { + if (license.checkFile(file)) { + passed++; + } else { + failed++; + String path = file.getPath(); + if (!license.foundLicense()) { + out.println( + license.tag + " LICENSE FAIL: " + path); + } + if (!license.foundCopyright()) { + out.println( + license.tag + " COPYRIGHT FAIL: " + path); + } + } + } + } + Visitor visitor = new Visitor(); + visitAll(visitor); + getOut().println( + "Total passed: " + + visitor.passed + + (visitor.failed == 0 ? "" : " failed: " + visitor.failed)); + if (failOnError && (0 < visitor.failed)) { + throw new BuildException( + failed + " files failed license check"); + } + } + + private void oldrun() throws BuildException { + if (list) { + list(); + return; + } + if (null == license) { + setLicense(DEFAULT); + } + final License license = this.license; // being paranoid... + if (null == license) { + throw new BuildException("no license"); + } + final PrintStream out = getOut(); + + List filelist = new ArrayList(); + String[] dirs = sourcepath.list(); + failed = 0; + passed = 0; + for (int i = 0; i < dirs.length; i++) { + int dirFailed = 0; + int dirPassed = 0; + File dir = project.resolveFile(dirs[i]); + String[] files = getDirectoryScanner(dir).getIncludedFiles(); + for (int j = 0; j < files.length; j++) { + File file = new File(dir, files[j]); + String path = file.getPath(); + if (path.endsWith(".java")) { + if (license.checkFile(file)) { + dirPassed++; + } else { + dirFailed++; + if (!license.foundLicense()) { + out.println( + license.tag + " LICENSE FAIL: " + path); + } + if (!license.foundCopyright()) { + out.println( + license.tag + " COPYRIGHT FAIL: " + path); + } + } + } + } + if (printDirectories) { + out.println( + "dir: " + + dirs[i] + + " passed: " + + dirPassed + + (dirFailed == 0 ? "" : " failed: " + dirFailed)); + } + failed += dirFailed; + passed += dirPassed; + } + } + + private void list() { + Enumeration enum = LICENSES.keys(); + StringBuffer sb = new StringBuffer(); + sb.append("known license keys:"); + boolean first = true; + while (enum.hasMoreElements()) { + sb.append((first ? " " : ", ") + enum.nextElement()); + if (first) { + first = false; + } + } + getOut().println(sb.toString()); + } + + + /** + * Encapsulate license and copyright specifications + * to check files use hokey string matching. + */ + public static class License { + /** acceptable years for copyright prefix to company - append " " */ + static final String[] YEARS = // XXX remove older after license xfer? + new String[] { "2002 ", "2003 ", "2001 ", "2000 ", "1999 " }; + public final String tag; + public final String license; + private final String[] copyright; + private boolean gotLicense; + private boolean gotCopyright; + + License(String tag, String license) { + this(tag, license, (String[]) null); + } + + License(String tag, String license, String copyright) { + this(tag, license, new String[] { copyright }); + } + + License( + String tag, + String license, + String copyright, + String altCopyright) { + this(tag, license, new String[] { copyright, altCopyright }); + } + + License(String tag, String license, String[] copyright) { + this.tag = tag; + if ((null == tag) || (0 == tag.length())) { + throw new IllegalArgumentException("null tag"); + } + this.license = license; + this.copyright = copyright; + } + + public final boolean gotValidFile() { + return foundLicense() && foundCopyright(); + } + + /** @return true if no license sought or if some license found */ + public final boolean foundLicense() { + return ((null == license) || gotLicense); + } + + /** @return true if no copyright sought or if some copyright found */ + public final boolean foundCopyright() { + return ((null == copyright) || gotCopyright); + } + + public boolean checkFile(final File file) { + clear(); + boolean result = false; + BufferedReader input = null; + int lineNum = 0; + try { + input = new BufferedReader(new FileReader(file)); + String line; + while (!gotValidFile() + && (line = input.readLine()) != null) { + lineNum++; + checkLine(line); + } + } catch (IOException e) { + System.err.println( + "reading line " + lineNum + " of " + file); + e.printStackTrace(System.err); + } finally { + if (null != input) { + try { + input.close(); + } catch (IOException e) { + } // ignore + } + } + return gotValidFile(); + } + + public String toString() { + return tag; + } + + private void checkLine(String line) { + if ((null == line) || (0 == line.length())) { + return; + } + if (!gotLicense + && (null != license) + && (-1 != line.indexOf(license))) { + gotLicense = true; + } + if (!gotCopyright && (null != copyright)) { + int loc; + for (int j = 0; + !gotCopyright && (j < YEARS.length); + j++) { + if (-1 != (loc = line.indexOf(YEARS[j]))) { + loc += YEARS[j].length(); + String afterLoc = line.substring(loc).trim(); + for (int i = 0; + !gotCopyright && (i < copyright.length); + i++) { + if (0 == afterLoc.indexOf(copyright[i])) { + gotCopyright = true; + } + } + } + } + } + } + + private void clear() { + if (gotLicense) { + gotLicense = false; + } + if (gotCopyright) { + gotCopyright = false; + } + } + } // class License +} + +class HeaderInfo { + /** File for which this is the info */ + public final File file; + + /** unmodifiable List of String years */ + public final List years; + + /** last line of license */ + public final int lastLine; + + /** last line of license */ + public final boolean hasLicense; + + public HeaderInfo(File file, int lastLine, List years, boolean hasLicense) { + this.lastLine = lastLine; + this.file = file; + this.hasLicense = hasLicense; + List newYears = new ArrayList(); + newYears.addAll(years); + Collections.sort(newYears); + this.years = Collections.unmodifiableList(newYears); + if ((null == file) || !file.canWrite()) { + throw new IllegalArgumentException("bad file: " + this); + } + if (!hasLicense) { + if ((0> lastLine) || (65 < lastLine)) { + throw new IllegalArgumentException("bad last line: " + this); + } + } else { + if ((null == years) || (1 > years.size())) { + throw new IllegalArgumentException("no years: " + this); + } + if ((20 > lastLine) || (65 < lastLine)) { + throw new IllegalArgumentException("bad last line: " + this); + } + } + } + + public String toString() { + return file.getPath() + ":" + lastLine + " " + years; + } + + public void writeHeader(PrintWriter writer) { + if (!hasLicense) { + writer.println(TOP); + writer.println(PARC_ONLY); + writeRest(writer); + } else { + final int size = years.size(); + if (1 > size) { + throw new Error("no years found in " + toString()); + } + String first = (String) years.get(0); + String last = (String) years.get(size-1); + boolean lastIs2002 = "2002".equals(last); + String xlast = last; + if (lastIs2002) { // 2002 was PARC + xlast = (String) (size > 1 ? years.get(size-2) : null); + // 1999-2002 Xerox implies 1999-2001 Xerox + if (first.equals(xlast) && !"2001".equals(xlast)) { + xlast = "2001"; + } + } + String xyears = first + "-" + xlast; + if (first.equals(last)) { + xyears = first; + } + + writer.println(TOP); + if (!lastIs2002) { // Xerox only + writer.println(XEROX_PREFIX + xyears + XEROX_SUFFIX + ". "); + } else if (size == 1) { // PARC only + writer.println(PARC_ONLY); + } else { // XEROX plus PARC + writer.println(XEROX_PREFIX + xyears + XEROX_SUFFIX + ", "); + writer.println(PARC); + } + writeRest(writer); + } + } + + void writeRest(PrintWriter writer) { + writer.println(" * All rights reserved. "); + writer.println(" * This program and the accompanying materials are made available "); + writer.println(" * under the terms of the Common Public License v1.0 "); + writer.println(" * which accompanies this distribution and is available at "); + writer.println(" * http://www.eclipse.org/legal/cpl-v10.html "); + writer.println(" * "); + writer.println(" * Contributors: "); + writer.println(" * Xerox/PARC initial implementation "); + writer.println(" * ******************************************************************/"); + writer.println(""); + } + + public static final String TOP + = "/* *******************************************************************"; + public static final String PARC + = " * 2002 Palo Alto Research Center, Incorporated (PARC)."; + public static final String PARC_ONLY + = " * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC)."; + public static final String XEROX_PREFIX + = " * Copyright (c) "; + public static final String XEROX_SUFFIX + = " Xerox Corporation"; + /* +/* ******************************************************************* + * Copyright (c) 1998-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ****************************************************************** + + */ +} + +/** + * header search/replace using hokey string matching + */ +class Header { + + /** replace the header in file */ + public static boolean replaceHeader(File file, HeaderInfo info) { + ArrayList years = new ArrayList(); + int endLine = 0; + BufferedReader input = null; + PrintWriter output = null; + FileWriter outWriter = null; + int lineNum = 0; + boolean result = false; + final File inFile = new File(file.getPath() + ".tmp"); + try { + File outFile = new File(file.getPath()); + if (!file.renameTo(inFile) || !inFile.canRead()) { + throw new Error("unable to rename " + file + " to " + inFile); + } + outWriter = new FileWriter(outFile); + input = new BufferedReader(new FileReader(inFile)); + output = new PrintWriter(outWriter, true); + info.writeHeader(output); + String line; + while (null != (line = input.readLine())) { + lineNum++; + if (lineNum > info.lastLine) { + output.println(line); + } + } + } catch (IOException e) { + System.err.println( + "writing line " + lineNum + " of " + file); + e.printStackTrace(System.err); + result = false; + } finally { + if (null != input) { + try { + input.close(); + } catch (IOException e) { + result = false; + } + } + if (null != outWriter) { + try { + outWriter.close(); + } catch (IOException e) { + result = false; + } + } + result = inFile.delete(); + } + return result; + } + + public static HeaderInfo checkFile(final File file) { + ArrayList years = new ArrayList(); + int endLine = 0; + BufferedReader input = null; + int lineNum = 0; + try { + input = new BufferedReader(new FileReader(file)); + String line; + while (null != (line = input.readLine())) { + lineNum++; + String ll = line.trim(); + if (ll.startsWith("package ") + || ll.startsWith("import ")) { + break; // ignore default package w/o imports + } + if (checkLine(line, years)) { + endLine = lineNum; + break; + } + } + } catch (IOException e) { + System.err.println( + "reading line " + lineNum + " of " + file); + e.printStackTrace(System.err); + } finally { + if (null != input) { + try { + input.close(); + } catch (IOException e) { + } // ignore + } + } + return new HeaderInfo(file, endLine, years, endLine > 0); + } + + /** + * Add any years found (as String) to years, + * and return true at the first end-of-comment + * @return true if this line has end-of-comment + */ + private static boolean checkLine(String line, ArrayList years) { + if ((null == line) || (0 == line.length())) { + return false; + } + int loc; + int start = 0; + + while ((-1 != (loc = line.indexOf("199", start)) + || (-1 != (loc = line.indexOf("200", start))))) { + char c = line.charAt(loc + 3); + if ((c <= '9') && (c >= '0')) { + years.add(line.substring(loc, loc+4)); + } + start = loc + 4; + } + + return (-1 != line.indexOf("*/")); + } + +} // class Header + diff --git a/build/src/org/aspectj/internal/tools/ant/taskdefs/ConditionalTask.java b/build/src/org/aspectj/internal/tools/ant/taskdefs/ConditionalTask.java new file mode 100644 index 000000000..8b5184121 --- /dev/null +++ b/build/src/org/aspectj/internal/tools/ant/taskdefs/ConditionalTask.java @@ -0,0 +1,182 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + +package org.aspectj.internal.tools.ant.taskdefs; + +import org.apache.tools.ant.*; +import java.util.*; + +public abstract class ConditionalTask extends Task { + + public final static String TRUE = "true"; + + private List ifs; + protected List ifs() { + return ifs != null ? ifs : (ifs = new Vector()); + } + + public If createIf() { + If i = new If(); + ifs().add(i); + return i; + } + + public If createIf(String name, String equals, boolean strict) { + If i = createIf(); + i.setName(name); + i.setEquals(equals); + i.setStrict(strict); + return i; + } + + public If createIf(String name, String equals) { + return createIf(name, equals, false); + } + + public If createIf(String name) { + return createIf(name, TRUE, false); + } + + public If createIf(String name, boolean strict) { + return createIf(name, TRUE, strict); + } + + public void setIfs(String ifs) { + StringTokenizer tok = new StringTokenizer(ifs, ",;: ", false); + while (tok.hasMoreTokens()) { + String next = tok.nextToken(); + int iequals = next.lastIndexOf("="); + String equals; + String name; + boolean strict; + If i = createIf(); + if (iequals != -1) { + name = next.substring(0, iequals); + equals = next.substring(iequals + 1); + strict = true; + } else { + name = next.substring(0); + equals = TRUE; + strict = false; + } + i.setName(name); + i.setEquals(equals); + i.setStrict(strict); + } + } + + public void setIf(String ifStr) { + setIfs(ifStr); + } + + public class If { + public If() { + this(null, null); + } + public If(String name) { + this(name, TRUE); + } + public If(String name, String equals) { + setName(name); + setEquals(equals); + } + private String name; + public void setName(String name) { + this.name = name; + } + public String getName() { + return name; + } + private String equals; + public void setEquals(String equals) { + this.equals = equals; + } + public String getEquals() { + return equals; + } + private boolean strict = false; + public void setStrict(boolean strict) { + this.strict = strict; + } + public boolean isStrict() { + return strict; + } + public boolean isOk(String prop) { + return isOk(prop, isStrict()); + } + //XXX Need a better boolean parser + public boolean isOk(String prop, boolean isStrict) { + if (isStrict) { + return prop != null && prop.equals(getEquals()); + } else { + if (isOk(prop, true)) { + return true; + } + if (prop == null || isFalse(getEquals())) { + return true; + } + if ( (isTrue(getEquals()) && isTrue(prop)) || + (isFalse(getEquals()) && isFalse(prop)) ) { + return true; + } + return false; + } + } + private boolean isFalse(String prop) { + return isOneOf(prop, falses) || isOneOf(prop, complement(trues)); + } + private boolean isTrue(String prop) { + return isOneOf(prop, trues) || isOneOf(prop, complement(falses)); + } + private boolean isOneOf(String prop, String[] strings) { + for (int i = 0; i < strings.length; i++) { + if (strings[i].equals(prop)) { + return true; + } + } + return false; + } + private String[] complement(String[] strings) { + for (int i = 0; i < strings.length; i++) { + strings[i] = "!" + strings[i]; + } + return strings; + } + } + + final static String[] falses = { "false", "no" }; + final static String[] trues = { "true", "yes" }; + + protected boolean checkIfs() { + return getFalses().size() == 0; + } + + protected List getFalses() { + Iterator iter = ifs().iterator(); + List result = new Vector(); + while (iter.hasNext()) { + If next = (If) iter.next(); + String name = next.getName(); + String prop = project.getProperty(name); + if (prop == null) { + prop = project.getUserProperty(name); + } + if (!next.isOk(prop)) { + result.add(name); + } + } + return result; + } + + public abstract void execute() throws BuildException; +} diff --git a/build/src/org/aspectj/internal/tools/ant/taskdefs/CopyAndInlineStylesheet.java b/build/src/org/aspectj/internal/tools/ant/taskdefs/CopyAndInlineStylesheet.java new file mode 100644 index 000000000..bafe5da2b --- /dev/null +++ b/build/src/org/aspectj/internal/tools/ant/taskdefs/CopyAndInlineStylesheet.java @@ -0,0 +1,113 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + +package org.aspectj.internal.tools.ant.taskdefs; + +import org.apache.tools.ant.*; +import org.apache.tools.ant.taskdefs.*; +import java.io.*; + +public class CopyAndInlineStylesheet extends Task { + + private File file; + public void setFile(String file) { + this.file = project.resolveFile(file); + } + + private File todir; + public void setTodir(String todir) { + this.todir = project.resolveFile(todir); + } + + + public void execute() throws BuildException { + try { + if (todir == null) { + throw new BuildException("must set 'todir' attribute"); + } + if (file == null) { + throw new BuildException("must set 'file' attribute"); + } + log("copying html from" + file + " to " + todir.getAbsolutePath()); + + File toFile = new File(todir, file.getName()); + + Mkdir mkdir = (Mkdir) project.createTask("mkdir"); + mkdir.setDir(todir); + mkdir.execute(); + + BufferedReader in = new BufferedReader(new FileReader(file)); + PrintStream out = new PrintStream(new FileOutputStream(toFile)); + + outer: + while (true) { + String line = in.readLine(); + if (line == null) break; + if (isStyleSheet(line)) { + doStyleSheet(line, out, file); + while (true) { + String line2 = in.readLine(); + if (line2 == null) break outer; + out.println(line2); + } + } else { + out.println(line); + } + } + + in.close(); + out.close(); + } catch (IOException e) { + throw new BuildException(e.getMessage()); + } + } + + private static void doStyleSheet(String line, PrintStream out, File file) throws IOException { + int srcIndex = line.indexOf("href"); + int startQuotIndex = line.indexOf('"', srcIndex); + int endQuotIndex = line.indexOf('"', startQuotIndex + 1); + + String stylesheetLocation = line.substring(startQuotIndex + 1, endQuotIndex); + + File styleSheetFile = new File(file.getParent(), stylesheetLocation); + + out.println("<style type=\"text/css\">"); + out.println("<!--"); + + BufferedReader inStyle = new BufferedReader(new FileReader(styleSheetFile)); + + while (true) { + String line2 = inStyle.readLine(); + if (line2 == null) break; + out.println(line2); + } + inStyle.close(); + + out.println("-->"); + out.println("</style>"); + } + + + private static boolean isStyleSheet(String line) throws IOException { + line = line.toLowerCase(); + int len = line.length(); + int i = 0; + + while (true) { + if (i == len) return false; + if (! Character.isWhitespace(line.charAt(i))) break; + } + + return line.startsWith("<link", i); + } +} diff --git a/build/src/org/aspectj/internal/tools/ant/taskdefs/StripNonBodyHtml.java b/build/src/org/aspectj/internal/tools/ant/taskdefs/StripNonBodyHtml.java new file mode 100644 index 000000000..0a019707c --- /dev/null +++ b/build/src/org/aspectj/internal/tools/ant/taskdefs/StripNonBodyHtml.java @@ -0,0 +1,233 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + +package org.aspectj.internal.tools.ant.taskdefs; + +import org.apache.tools.ant.*; +import org.apache.tools.ant.taskdefs.*; +import java.io.*; + +/** + * Task to convert html source files into files with only body content. + * + * <p> This task can take the following arguments:</p> + * + * <ul> + * <li>srcdir</li> + * <li>destdir</li> + * <li>include</li> + * <li>exclude</li> + * </ul> + * + * <p>Of these arguments, only <b>sourcedir</b> is required.</p> + * + * <p> When this task executes, it will scan the srcdir based on the + * include and exclude properties.</p> + */ + +public class StripNonBodyHtml extends MatchingTask { + + private File srcDir; + private File destDir = null; + + public void setSrcdir(File srcDir) { + this.srcDir = srcDir; + } + + public void setDestdir(File destDir) { + this.destDir = destDir; + } + + public void execute() throws BuildException { + if (srcDir == null) { + throw new BuildException("srcdir attribute must be set!"); + } + if (!srcDir.exists()) { + throw new BuildException("srcdir does not exist!"); + } + if (!srcDir.isDirectory()) { + throw new BuildException("srcdir is not a directory!"); + } + if (destDir != null) { + if (!destDir.exists()) { + throw new BuildException("destdir does not exist!"); + } + if (!destDir.isDirectory()) { + throw new BuildException("destdir is not a directory!"); + } + } + + DirectoryScanner ds = super.getDirectoryScanner(srcDir); + String[] files = ds.getIncludedFiles(); + + log("stripping " + files.length + " files"); + int stripped = 0; + for (int i = 0, len = files.length; i < len; i++) { + if (processFile(files[i])) { + stripped++; + } else { + log(files[i] + " not stripped"); + } + } + log(stripped + " files successfully stripped"); + } + + boolean processFile(String filename) throws BuildException { + File srcFile = new File(srcDir, filename); + File destFile; + if (destDir == null) { + destFile = srcFile; + } else { + destFile = new File(destDir, filename); + destFile.getParentFile().mkdirs(); + } + try { + return strip(srcFile, destFile); + } catch (IOException e) { + throw new BuildException(e); + } + } + + private boolean strip(File f, File g) throws IOException { + BufferedInputStream in = + new BufferedInputStream(new FileInputStream(f)); + String s = readToString(in); + in.close(); + return writeBodyTo(s, g); + } + + private ByteArrayOutputStream temp = new ByteArrayOutputStream(); + private byte[] buf = new byte[2048]; + + private String readToString(InputStream in) throws IOException { + ByteArrayOutputStream temp = this.temp; + byte[] buf = this.buf; + String s = ""; + try { + while (true) { + int i = in.read(buf, 0, 2048); + if (i == -1) break; + temp.write(buf, 0, i); + + } + s = temp.toString(); + } finally { + temp.reset(); + } + return s; + } + + private boolean writeBodyTo(String s, File f) throws IOException { + int start, end; + try { + start = findStart(s); + end = findEnd(s, start); + } catch (ParseException e) { + return false; // if we get confused, just don't write the file. + } + s = processBody(s,f); + BufferedOutputStream out = + new BufferedOutputStream(new FileOutputStream(f)); + + out.write(s.getBytes()); + out.close(); + return true; + } + + /** + * Process body. This implemenation strips text + * between <!-- start strip --> + * and <!-- end strip --> + * inclusive. + */ + private String processBody(String body, File file) { + if (null == body) return body; + final String START = "<!-- start strip -->"; + final String END = "<!-- end strip -->"; + return stripTags(body, file.toString(), START, END); + } + + /** + * Strip 0..n substrings in input: "s/${START}.*${END}//g" + * @param input the String to strip + * @param source the name of the source for logging purposes + * @param start the starting tag (case sensitive) + * @param end the ending tag (case sensitive) + */ + String stripTags(String input, final String SOURCE, + final String START, final String END) { + if (null == input) return input; + StringBuffer buffer = new StringBuffer(input.length()); + String result = input; + int curLoc = 0; + while (true) { + int startLoc = input.indexOf(START, curLoc); + if (-1 == startLoc) { + buffer.append(input.substring(curLoc)); + result = buffer.toString(); + break; // <------------ valid exit + } else { + int endLoc = input.indexOf(END, startLoc); + if (-1 == endLoc) { + log(SOURCE + " stripTags - no end tag - startLoc=" + startLoc); + break; // <------------ invalid exit + } else if (endLoc < startLoc) { + log(SOURCE + " stripTags - impossible: startLoc=" + + startLoc + " > endLoc=" + endLoc); + break; // <------------ invalid exit + } else { + buffer.append(input.substring(curLoc, startLoc)); + curLoc = endLoc + END.length(); + } + } + } + return result; + } + + private int findStart(String s) throws ParseException { + int len = s.length(); + int start = 0; + while (true) { + start = s.indexOf("<body", start); + if (start == -1) { + start = s.indexOf("<BODY", start); + if (start == -1) throw barf(); + } + start = start + 5; + if (start >= len) throw barf(); + char ch = s.charAt(start); + if (ch == '>') return start + 1; + if (Character.isWhitespace(ch)) { + start = s.indexOf('>', start); + if (start == -1) return -1; + return start + 1; + } + } + } + + private int findEnd(String s, int start) throws ParseException { + int end; + end = s.indexOf("</body>", start); + if (end == -1) { + end = s.indexOf("</BODY>", start); + if (end == -1) throw barf(); + } + return end; + } + + private static class ParseException extends Exception {} + + private static ParseException barf() { + return new ParseException(); + } +} diff --git a/build/src/org/aspectj/internal/tools/ant/taskdefs/TestBuildModule.java b/build/src/org/aspectj/internal/tools/ant/taskdefs/TestBuildModule.java new file mode 100644 index 000000000..374f71d79 --- /dev/null +++ b/build/src/org/aspectj/internal/tools/ant/taskdefs/TestBuildModule.java @@ -0,0 +1,79 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.internal.tools.ant.taskdefs; + +import org.apache.tools.ant.Project; +import org.aspectj.internal.tools.build.*; +import org.aspectj.internal.tools.build.BuildSpec; +import org.aspectj.internal.tools.build.Util; + +import java.io.File; +import java.util.Arrays; + +public class TestBuildModule { + private static boolean REBUILD = false; + private static final String SYNTAX = "java {classname} <[product|module]dir>"; + public static void main(String[] args) { + + if ((null == args) || (1 > args.length) + || !Util.canReadDir(new File(args[0]))) { + System.err.println(SYNTAX); + return; + } + File dir = new File(args[0]); + // create a module + if (Util.canReadDir(new File(dir, "dist"))) { + createProduct(args); + } else if (Util.canReadFile(new File(dir, ".classpath"))) { + createModule(args); + } else { + System.err.println(SYNTAX); + } + } + + static void createModule(String[] args) { + File moduleDir = new File(args[0]); + File baseDir = moduleDir.getParentFile(); + if (null == baseDir) { + baseDir = new File("."); + } + File jarDir = new File(baseDir, "aj-build-jars"); + if (!(Util.canReadDir(jarDir) || jarDir.mkdirs())) { + System.err.println("unable to create " + jarDir); + return; + } + + // set module dir or basedir plus module name + BuildSpec buildSpec = new BuildSpec(); + buildSpec.moduleDir = moduleDir; + buildSpec.jarDir = jarDir; + buildSpec.verbose = true; + buildSpec.failonerror = true; + buildSpec.trimTesting = true; + buildSpec.rebuild = true; + + File tempDir = null; + Project project = new Project(); + project.setProperty("verbose", "true"); + project.setName("TestBuildModule.createModule" + Arrays.asList(args)); + Builder builder = AntBuilder.getBuilder("", project, tempDir); + builder.build(buildSpec); + } + + static void createProduct(String[] args) { + throw new Error("unimplemented"); + } +} + diff --git a/build/src/org/aspectj/internal/tools/ant/taskdefs/taskdefs.properties b/build/src/org/aspectj/internal/tools/ant/taskdefs/taskdefs.properties new file mode 100644 index 000000000..37da66e9e --- /dev/null +++ b/build/src/org/aspectj/internal/tools/ant/taskdefs/taskdefs.properties @@ -0,0 +1,20 @@ +ajinstaller=org.aspectj.internal.tools.ant.taskdefs.AJInstaller +ajpush=org.aspectj.internal.tools.ant.taskdefs.AJPush +ajbuild=org.aspectj.internal.tools.ant.taskdefs.BuildModule +checklics=org.aspectj.internal.tools.ant.taskdefs.Checklics +stripnonbodyhtml=org.aspectj.internal.tools.ant.taskdefs.StripNonBodyHtml + +# ajclean=org.aspectj.internal.tools.ant.taskdefs.AJclean +# ajcvs=org.aspectj.internal.tools.ant.taskdefs.Ajcvs +# ajikes=org.aspectj.internal.tools.ant.taskdefs.AJikes +# ajinit=org.aspectj.internal.tools.ant.taskdefs.AjInit +# checkrelease=org.aspectj.internal.tools.ant.taskdefs.Checkrelease +# clear=org.aspectj.internal.tools.ant.taskdefs.Clear +# inlinestylesheetaj=org.aspectj.internal.tools.ant.taskdefs.CopyAndInlineStyleshee +# ensure=org.aspectj.internal.tools.ant.taskdefs.Ensure +# ensureproperties=org.aspectj.internal.tools.ant.taskdefs.EnsureProperties +# newdir=org.aspectj.internal.tools.ant.taskdefs.Newdir +# overwrite=org.aspectj.internal.tools.ant.taskdefs.Overwrite +# props2filters=org.aspectj.internal.tools.ant.taskdefs.Props2Filters +# tgz=org.aspectj.internal.tools.ant.taskdefs.Tgz +# vmcheck=org.aspectj.internal.tools.ant.taskdefs.VMCheck diff --git a/build/src/org/aspectj/internal/tools/build/BuildSpec.java b/build/src/org/aspectj/internal/tools/build/BuildSpec.java new file mode 100644 index 000000000..bf9ccdea8 --- /dev/null +++ b/build/src/org/aspectj/internal/tools/build/BuildSpec.java @@ -0,0 +1,161 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.internal.tools.build; + + +import java.io.File; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Properties; + +/** + * Open struct for specifying builds for both modules and products. + * Separated from bulder to permit this to build many modules + * concurrently. + * Static state has much of the Ant build properties (move?) + */ +public class BuildSpec { + public static final String baseDir_NAME = "aspectj.modules.dir"; + public static final String stagingDir_NAME = "aj.staging.dir"; + public static final String jarDir_NAME = "aj.jar.dir"; + public static final String tempDir_NAME = "aj.temp.dir"; + public static final String distDir_NAME = "aj.dist.dir"; + + /** name of a system property for reading the build version */ + public static final String SYSTEM_BUILD_VERSION_KEY = "aspectj.build.version"; + + /** value of the build.version if build version not defined */ + public static final String BUILD_VERSION_DEFAULT = "DEVELOPMENT"; + + /** name of a filter property for the normal build version */ + public static final String BUILD_VERSION_NAME = "build.version"; + + /** name of a filter property for the company */ + public static final String COMPANY_NAME = "company.name"; + + /** default value of of a filter property for the company */ + public static final String COMPANY_NAME_DEFAULT = "aspectj.org"; + + /** name of a filter property for the base build version (no alpha, etc.) */ + public static final String BUILD_VERSION_BASE_NAME = "build.version.base"; + + /** copyright property name */ + public static final String COPYRIGHT_NAME = "copyright.allRights.from1998"; + + /** overall copyright */ + public static final String COPYRIGHT = + "Copyright (c) 1998-2001 Xerox Corporation, " + + "2002 Palo Alto Research Center, Incorporated. All rights reserved."; + + /** name of a filter property for the long build version (alpha) */ + public static final String BUILD_VERSION_LONG_NAME = "build.version.long"; + + /** name of a filter property for the short build version (alpha -> a, etc.) */ + public static final String BUILD_VERSION_SHORT_NAME = "build.version.short"; + + /** name of a filter property for the build time */ + public static final String BUILD_TIME_NAME = "build.time"; + + /** name of a filter property for the build date */ + public static final String BUILD_DATE_NAME = "build.date"; + + /** lazily and manually generate properties */ + public static Properties getFilterProperties(long time, String longVersion) { + if (time < 1) { + time = System.currentTimeMillis(); + } + if ((null == longVersion) || (0 == longVersion.length())) { + longVersion = System.getProperty( + BuildSpec.SYSTEM_BUILD_VERSION_KEY, + BuildSpec.BUILD_VERSION_DEFAULT); + } + Properties filterProps = new Properties(); + + // build time and date XXX set in build script? + String timeString = time+"L"; + // XXX wrong date format - use Version.java template format? + String date = new SimpleDateFormat("MMMM d, yyyy").format(new Date()); + filterProps.setProperty(BUILD_TIME_NAME, timeString); + filterProps.setProperty(BUILD_DATE_NAME, date); + + // build version, short build version, and base build version + // 1.1alpha1, 1.1a1, and 1.1 + String key = BuildSpec.BUILD_VERSION_NAME; + String value = longVersion; + value = value.trim(); + filterProps.setProperty(key, value); + + key = BuildSpec.BUILD_VERSION_LONG_NAME; + filterProps.setProperty(key, value); + + if (!BuildSpec.BUILD_VERSION_DEFAULT.equals(value)) { + value = Util.shortVersion(value); + } + key = BuildSpec.BUILD_VERSION_SHORT_NAME; + filterProps.setProperty(key, value); + + key = BuildSpec.BUILD_VERSION_BASE_NAME; + if (!BuildSpec.BUILD_VERSION_DEFAULT.equals(value)) { + int MAX = value.length(); + for (int i = 0; i < MAX; i++) { + char c = value.charAt(i); + if ((c != '.') && ((c < '0') || (c > '9'))) { + value = value.substring(0,i); + break; + } + } + } + filterProps.setProperty(key, value); + + // company name, copyright XXX fix company name + key = BuildSpec.COMPANY_NAME; + value = System.getProperty(key, BuildSpec.COMPANY_NAME_DEFAULT); + filterProps.setProperty(key, value); + filterProps.setProperty(BuildSpec.COPYRIGHT_NAME, BuildSpec.COPYRIGHT); + + return filterProps; + } + + // shared + public File baseDir; + public File moduleDir; + public File jarDir; + public File tempDir; + public File stagingDir; + public String buildConfig; + public String version; + public boolean rebuild; + public boolean trimTesting; + public boolean assembleAll; + public boolean failonerror; + public boolean verbose; + + // building products + public File productDir; + public boolean createInstaller; + public File distDir; + + // building modules + public String module; + + public String toString() { // XXX better + if (null != productDir) { + return "product " + productDir.getName(); + } else { + return "module " + moduleDir.getName(); + } + } +} + diff --git a/build/src/org/aspectj/internal/tools/build/Builder.java b/build/src/org/aspectj/internal/tools/build/Builder.java new file mode 100644 index 000000000..19c2ecb4b --- /dev/null +++ b/build/src/org/aspectj/internal/tools/build/Builder.java @@ -0,0 +1,497 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.internal.tools.build; + +import org.apache.tools.ant.BuildException; + +import java.io.File; +import java.io.FileFilter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; +import java.util.Properties; + +/** + * Template class to build (eclipse) modules (and, weakly, products), + * including any required modules. + * When building modules, this assumes: + * <ul> + * <li>the name of the module is the base name of the module directory</li> + * <li>all module directories are in the same base (workspace) directory</li> + * <li>the name of the target module jar is {moduleName}.jar</li> + * <li>a module directory contains a <code>.classpath</code> file with + * (currently line-parseable) entries per Eclipse (XML) conventions</li> + * <li><code>Builder.RESOURCE_PATTERN</code> + * identifies all resources to copy to output.</li> + * <li>This can safely trim test-related code: + * <ul> + * <li>source directories named "testsrc"</li> + * <li>libraries named "junit.jar"</li> + * <li>required modules whose names start with "testing"</li> + * </ul> + * <li>A file <code>{moduleDir}/{moduleName}.properties</code> + * is a property file possibly + * containing entries defining requirements to be merged with the output jar + * (deprecated mechanism - use assembleAll or products)</li> + * </ul> + * This currently provides no control over the compile or assembly process, + * but clients can harvest <code>{moduleDir}/bin</code> directories to re-use + * the results of eclipse compiles. + * <p> + * When building products, this assumes: + * <ul> + * <li>the installer-resources directory is a peer of the products directory, + * itself the parent of the particular product directory.</li> + * <li>the dist, jar, product, and base (module) directory are set</li> + * <li>the product distribution consists of all (and only) the files + * in the dist sub-directory of the product directory</li> + * <li>files in the dist sub-directory that are empty and end with .jar + * represent modules to build, either as named or through aliases + * known here.</li> + * <li>When assembling the distribution, all non-binary files are to + * be filtered.<li> + * <li>the name of the product installer is aspectj-{productName}-{version}.jar, + * where {productName} is the base name of the product directory</li> + * </ul> + * <p> + * When run using main(String[]), all relevant Ant libraries and properties + * must be defined. + * <p> + * Written to compile standalone. Refactor if using utils, bridge, etc. + */ +public abstract class Builder { + + /** + * This has only weak forms for build instructions needed: + * - resource pattern + * - compiler selection and control + * + * Both assumed and generated paths are scattered; + * see XXXNameLiteral and XXXFileLiteral. + */ + + public static final String RESOURCE_PATTERN + = "**/*.txt,**/*.rsc,**/*.gif,**/*.properties"; + + public static final String BINARY_SOURCE_PATTERN + = "**/*.rsc,**/*.gif,**/*.jar,**/*.zip"; + + public static final String ALL_PATTERN = "**/*"; + + /** enable copy filter semantics */ + protected static final boolean FILTER_ON = true; + + /** disable copy filter semantics */ + protected static final boolean FILTER_OFF = false; + + protected final Messager handler; + protected boolean buildingEnabled; + + private final File tempDir; + private final ArrayList tempFiles; + private final boolean useEclipseCompiles; + + protected boolean verbose; + protected Properties filterProps; + protected boolean filterSetup; + + + protected Builder(File tempDir, boolean useEclipseCompiles, + Messager handler) { + Util.iaxIfNull(handler, "handler"); + this.useEclipseCompiles = useEclipseCompiles; + this.handler = handler; + this.tempFiles = new ArrayList(); + if ((null == tempDir) || !tempDir.canWrite() || !tempDir.isDirectory()) { + this.tempDir = Util.makeTempDir("Builder"); + } else { + this.tempDir = tempDir; + } + buildingEnabled = true; + } + + /** tell builder to stop or that it's ok to run */ + public void setBuildingEnabled(boolean enabled) { + buildingEnabled = enabled; + // XXX support user cancels in eclipse... + } + + public void setVerbose(boolean verbose) { + this.verbose = verbose; + } + + public boolean build(BuildSpec buildSpec) { + if (!buildingEnabled) { + return false; + } + + if (null == buildSpec.productDir) { // ensure module properties + // derive moduleDir from baseDir + module + if (null == buildSpec.moduleDir) { + if (null == buildSpec.baseDir) { + throw new BuildException("require baseDir or moduleDir"); + } else if (null == buildSpec.module) { + throw new BuildException("require module with baseDir"); + } else { + if (null == buildSpec.baseDir) { + buildSpec.baseDir = new File("."); // user.home? + } + buildSpec.moduleDir = new File(buildSpec.baseDir, buildSpec.module); + } + } else if (null == buildSpec.baseDir) { // derive baseDir from moduleDir parent + buildSpec.baseDir = buildSpec.moduleDir.getParentFile(); // rule: base is parent + if (null == buildSpec.baseDir) { + buildSpec.baseDir = new File("."); // user.home? + } + } + Util.iaxIfNotCanReadDir(buildSpec.moduleDir, "moduleDir"); + + if (null == buildSpec.module) { // derive module name from directory + buildSpec.module = buildSpec.moduleDir.getName(); + if (null == buildSpec.module) { + throw new BuildException("no name, even from " + buildSpec.moduleDir); + } + } + } + + if (null != buildSpec.productDir) { + return buildProduct(buildSpec); + } + if (buildSpec.trimTesting && (-1 != buildSpec.module.indexOf("testing"))) { // XXXNameLiteral + String warning = "Warning - cannot trimTesting for testing modules: "; + handler.log(warning + buildSpec.module); + } + Messager handler = new Messager(); + Modules modules = new Modules(buildSpec.baseDir, buildSpec.jarDir, buildSpec.trimTesting, handler); + + final Module moduleToBuild = modules.getModule(buildSpec.module); + ArrayList errors = new ArrayList(); + try { + return buildAll( + moduleToBuild, + errors, + buildSpec.rebuild, + buildSpec.assembleAll); + } finally { + if (0 < errors.size()) { + String label = "error building " + buildSpec + ": "; + for (Iterator iter = errors.iterator(); iter.hasNext();) { + handler.error(label + iter.next()); + } + } + } + } + + /** + * Clean up any temporary files, etc. after build completes + */ + public boolean cleanup() { + boolean noErr = true; + for (ListIterator iter = tempFiles.listIterator(); iter.hasNext();) { + File file = (File) iter.next(); + if (!Util.deleteContents(file) || !file.delete()) { + if (noErr) { + noErr = false; + } + handler.log("unable to clean up " + file); + } + } + return noErr; + } + + /** + * Build a module with all antecedants. + * @param module the Module to build + * @param errors the List sink for errors, if any + * @return false after successful build, when module jar should exist + */ + protected boolean buildAll(Module module, List errors, boolean rebuild, boolean assembleAll) { + String[] buildList = getAntecedantModuleNames(module, rebuild); + if ((null != buildList) && (0 < buildList.length)) { + final Modules modules = module.getModules(); + final Messager handler = this.handler; + final boolean log = (verbose && (null != handler)); + final boolean verbose = this.verbose; + if (log) { + handler.log("modules to build: " + Arrays.asList(buildList)); + } + for (int i = 0; i < buildList.length; i++) { + + if (!buildingEnabled) { + return false; + } + String modName = buildList[i]; + if (log) { + handler.log("building " + modName); + } + Module next = modules.getModule(modName); + if (!buildOnly(next, errors)) { + return false; + } + } + } + if (assembleAll && !assembleAll(module, handler)) { + return false; + } + return true; + } + + /** + * Build a module but no antecedants. + * @param module the Module to build + * @param errors the List sink for errors, if any + * @return false after successful build, when module jar should exist + */ + protected boolean buildOnly(Module module, List errors) { + if (!buildingEnabled) { + return false; + } + File classesDir = useEclipseCompiles + ? new File(module.moduleDir, "bin") // XXXFileLiteral + : new File(tempDir, "classes-" + System.currentTimeMillis()); + if (verbose) { + handler.log("buildOnly " + module); + } + try { + return (useEclipseCompiles || compile(module, classesDir, errors)) + && assemble(module, classesDir, errors); + } finally { + if (!useEclipseCompiles + && (!Util.deleteContents(classesDir) || !classesDir.delete())) { + errors.add("unable to delete " + classesDir); + } + } + } + + /** + * Register temporary file or directory to be deleted when + * the build is complete, even if an Exception is thrown. + */ + protected void addTempFile(File tempFile) { + if (null != tempFile) { + tempFiles.add(tempFile); + } + } + /** + * Build product by discovering any modules to build, + * building those, assembling the product distribution, + * and optionally creating an installer for it. + * @return true on success + */ + protected boolean buildProduct(BuildSpec buildSpec) + throws BuildException { + Util.iaxIfNull(buildSpec, "buildSpec"); + // XXX if installer and not out of date, do not rebuild unless rebuild set + + if (!buildSpec.trimTesting) { + buildSpec.trimTesting = true; + handler.log("testing trimmed for " + buildSpec); + } + Util.iaxIfNotCanReadDir(buildSpec.productDir, "productDir"); + Util.iaxIfNotCanReadDir(buildSpec.baseDir, "baseDir"); + Util.iaxIfNotCanWriteDir(buildSpec.distDir, "distDir"); + + // ---- discover modules to build, and build them + Modules modules = new Modules( + buildSpec.baseDir, + buildSpec.jarDir, + buildSpec.trimTesting, + handler); + ProductModule[] productModules = discoverModules(buildSpec.productDir, modules); + for (int i = 0; i < productModules.length; i++) { + if (buildSpec.verbose) { + handler.log("building product module " + productModules[i]); + } + if (!buildProductModule(productModules[i])) { + return false; + } + } + if (buildSpec.verbose) { + handler.log("assembling product module for " + buildSpec); + } + + // ---- assemble product distribution + final String productName = buildSpec.productDir.getName(); + final File targDir = new File(buildSpec.distDir, productName); + final String targDirPath = targDir.getPath(); + if (targDir.canWrite()) { + Util.deleteContents(targDir); + } + + if (!targDir.canWrite() && !targDir.mkdirs()) { + if (buildSpec.verbose) { + handler.log("unable to create " + targDir); + } + return false; + } + // filter-copy everything but the binaries + File distDir = new File(buildSpec.productDir, "dist"); // XXXFileLiteral + String excludes = Builder.BINARY_SOURCE_PATTERN; + String includes = Builder.ALL_PATTERN; + if (!copyFiles(distDir, targDir, includes, excludes, FILTER_ON)) { + return false; + } + + // copy binaries (but not module flag files) + excludes = null; + { + StringBuffer buf = new StringBuffer(); + for (int i = 0; i < productModules.length; i++) { + if (0 < buf.length()) { + buf.append(","); + } + buf.append(productModules[i].relativePath); + } + if (0 < buf.length()) { + excludes = buf.toString(); + } + } + includes = Builder.BINARY_SOURCE_PATTERN; + if (!copyFiles(distDir, targDir, includes, excludes, FILTER_OFF)) { + return false; + } + + // copy binaries associated with module flag files + for (int i = 0; i < productModules.length; i++) { + ProductModule product = productModules[i]; + String targPath = targDirPath + "/" + product.relativePath; + File jarFile = (product.assembleAll + ? product.module.getAssembledJar() + : product.module.getModuleJar() ); + copyFile(jarFile, new File(targPath), FILTER_OFF); + } + handler.log("created product in " + targDir); + + // ---- create installer + if (buildSpec.createInstaller) { + return buildInstaller(buildSpec, targDirPath); + } else { + return true; + } + } + + protected boolean buildProductModule(ProductModule module) { + boolean noRebuild = false; + ArrayList errors = new ArrayList(); + try { + return buildAll(module.module, errors, noRebuild, module.assembleAll); + } finally { + for (Iterator iter = errors.iterator(); iter.hasNext();) { + handler.error("error building " + module + ": " + iter.next()); + } + } + } + + /** + * Discover any modules that might need to be built + * in order to assemble the product distribution. + * This interprets empty .jar files as module deliverables. + */ + protected ProductModule[] discoverModules(File productDir, Modules modules) { + final ArrayList found = new ArrayList(); + FileFilter filter = new FileFilter() { + public boolean accept(File file) { + if ((null != file) + && file.canRead() + && file.getPath().endsWith(".jar") // XXXFileLiteral + && (0l == file.length())) { + found.add(file); + } + return true; + } + }; + Util.visitFiles(productDir, filter); + ArrayList results = new ArrayList(); + for (Iterator iter = found.iterator(); iter.hasNext();) { + File file = (File) iter.next(); + String jarName = moduleAliasFor(file.getName().toLowerCase()); + if (jarName.endsWith(".jar") || jarName.endsWith(".zip")) { // XXXFileLiteral + jarName = jarName.substring(0, jarName.length()-4); + } else { + handler.log("can only replace .[jar|zip]: " + file); // XXX error? + } + boolean assembleAll = jarName.endsWith("-all"); // XXXFileLiteral + String name = (!assembleAll ? jarName : jarName.substring(0, jarName.length()-4)); + Module module = modules.getModule(name); + if (null == module) { + handler.log("unable to find module for " + file); + } else { + results.add(new ProductModule(productDir, file, module, assembleAll)); + } + } + return (ProductModule[]) results.toArray(new ProductModule[0]); + } + + /** + * Map delivered-jar name to created-module name + * @param jarName the String (lowercased) of the jar/zip to map + */ + protected String moduleAliasFor(String jarName) { + if ("aspectjtools.jar".equals(jarName)) { // XXXFileLiteral + return "ajbrowser-all.jar"; + } else if ("aspectjrt.jar".equals(jarName)) { + return "runtime.jar"; + } else { + return jarName; + } + } + + /** + * @return String[] names of modules to build for this module + */ + abstract protected String[] getAntecedantModuleNames(Module toBuild, boolean rebuild); + + /** + * Compile module classes to classesDir, saving String errors. + */ + abstract protected boolean compile(Module module, File classesDir, List errors); + + /** + * Assemble the module distribution from the classesDir, saving String errors. + */ + abstract protected boolean assemble(Module module, File classesDir, List errors); + + /** + * Assemble the module distribution from the classesDir and all antecendants, + * saving String errors. + */ + abstract protected boolean assembleAll(Module module, Messager handler); + + /** + * Generate the installer for this product to targDirPath + */ + abstract protected boolean buildInstaller(BuildSpec buildSpec, String targDirPath); + + /** + * Copy fromFile to toFile, optionally filtering contents + */ + abstract protected boolean copyFile(File fromFile, File toFile, boolean filter); + + /** + * Copy toDir any fromDir included files without any exluded files, + * optionally filtering contents. + * @param fromDir File dir to read from - error if not readable + * @param toDir File dir to write to - error if not writable + * @param included String Ant pattern of included files (if null, include all) + * @param excluded String Ant pattern of excluded files (if null, exclude none) + * @param filter if FILTER_ON, then filter file contents using global token/value pairs + */ + abstract protected boolean copyFiles(File fromDir, File toDir, String included, String excluded, boolean filter); +} + + + diff --git a/build/src/org/aspectj/internal/tools/build/Messager.java b/build/src/org/aspectj/internal/tools/build/Messager.java new file mode 100644 index 000000000..c7b12d4a9 --- /dev/null +++ b/build/src/org/aspectj/internal/tools/build/Messager.java @@ -0,0 +1,43 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.internal.tools.build; + +/** logging stub XXX replace */ +public class Messager { + public Messager() { + } + public boolean log(String s) { + System.out.println(s); + return true; + } + + public boolean error(String s) { + System.out.println(s); + return true; + } + + public boolean logException(String context, Throwable thrown) { + System.err.println(context); + thrown.printStackTrace(System.err); + return true; + } +} + + + + + + + diff --git a/build/src/org/aspectj/internal/tools/build/Module.java b/build/src/org/aspectj/internal/tools/build/Module.java new file mode 100644 index 000000000..9c6022d18 --- /dev/null +++ b/build/src/org/aspectj/internal/tools/build/Module.java @@ -0,0 +1,501 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.internal.tools.build; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileReader; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; +import java.util.Properties; +import java.util.StringTokenizer; + +/** + * This represents an (eclipse) build module/unit + * used by a Builder to compile classes + * and/or assemble zip file + * of classes, optionally with all antecedants. + * This implementation infers attributes from two + * files in the module directory: + * <ul> + * <li>an Eclipse project <code>.classpath</code> file + * containing required libraries and modules + * (collectively, "antecedants") + * </li> + * <li>a file <code>{moduleName}.mf.txt</code> is taken as + * the manifest of any .jar file produced, after filtering. + * </li> + * </ul> + * + * @see Builder + * @see Modules#getModule(String) + */ +public class Module { + + /** @return true if file is null or cannot be read or was + * last modified after time + */ + private static boolean outOfDate(long time, File file) { + return ((null == file) + || !file.canRead() + || (file.lastModified() > time)); + } + + /** @return all source files under srcDir */ + private static Iterator sourceFiles(File srcDir) { + ArrayList result = new ArrayList(); + sourceFiles(srcDir, result); + return result.iterator(); + } + + private static void sourceFiles(File srcDir, List result) { + if ((null == srcDir) || !srcDir.canRead() || !srcDir.isDirectory()) { + return; + } + File[] files = srcDir.listFiles(); + for (int i = 0; i < files.length; i++) { + if (files[i].isDirectory()) { + sourceFiles(files[i], result); + } else if (isSourceFile(files[i])) { + result.add(files[i]); + } + } + } + + /** + * Recursively find antecedant jars. + * @see findKnownJarAntecedants() + */ + private static void doFindKnownJarAntecedants(Module module, ArrayList known) { + Util.iaxIfNull(module, "module"); + Util.iaxIfNull(known, "known"); + + for (Iterator iter = module.getLibJars().iterator(); iter.hasNext();) { + File libJar = (File) iter.next(); + if (!skipLibraryJarAntecedant(libJar) + && !known.contains(libJar)) { // XXX what if same referent, diff path... + known.add(libJar); + } + } + for (Iterator iter = module.getRequired().iterator(); iter.hasNext();) { + Module required = (Module) iter.next(); + File requiredJar = required.getModuleJar(); + if (!known.contains(requiredJar)) { + known.add(requiredJar); + doFindKnownJarAntecedants(required, known); + } + } + } + + /** XXX gack explicitly skip Ant */ + private static boolean skipLibraryJarAntecedant(File libJar) { + if (null == libJar) { + return true; + } + String path = libJar.getPath().replace('\\', '/'); + return (-1 == path.indexOf("/lib/ant/lib/")); + } + + /**@return true if this is a source file */ + private static boolean isSourceFile(File file) { + String path = file.getPath(); + return (path.endsWith(".java") || path.endsWith(".aj")); // XXXFileLiteral + } + + public final boolean valid; + + public final File moduleDir; + + public final String name; + + /** reference back to collection for creating required modules */ + final Modules modules; + + /** path to output jar - may not exist */ + private final File moduleJar; + + /** path to fully-assembed jar - may not exist */ + private final File assembledJar; + + /** File list of library jars */ + private final List libJars; + + /** File list of source directories */ + private final List srcDirs; + + /** properties from the modules {name}.properties file */ + private final Properties properties; + + /** Module list of required modules */ + private final List required; + + /** List of File that are newer than moduleJar. Null until requested */ + //private List newerFiles; + /** true if this has been found to be out of date */ + private boolean outOfDate; + + /** true if we have calculated whether this is out of date */ + private boolean outOfDateSet; + + /** if true, trim testing-related source directories, modules, and libraries */ + private final boolean trimTesting; + + /** logger */ + private final Messager messager; + + Module(File moduleDir, + File jarDir, + String name, + Modules modules, + boolean trimTesting, + Messager messager) { + Util.iaxIfNotCanReadDir(moduleDir, "moduleDir"); + Util.iaxIfNotCanReadDir(jarDir, "jarDir"); + Util.iaxIfNull(name, "name"); + Util.iaxIfNull(modules, "modules"); + this.moduleDir = moduleDir; + this.trimTesting = trimTesting; + this.libJars = new ArrayList(); + this.required = new ArrayList(); + this.srcDirs = new ArrayList(); + this.properties = new Properties(); + this.name = name; + this.modules = modules; + this.messager = messager; + this.moduleJar = new File(jarDir, name + ".jar"); + this.assembledJar = new File(jarDir, name + "-all.jar"); + valid = init(); + } + + /** @return path to output jar - may not exist */ + public File getModuleJar() { + return moduleJar; + } + + /** @return path to output assembled jar - may not exist */ + public File getAssembledJar() { + return assembledJar; + } + + /** @return unmodifiable List of required modules String names*/ + public List getRequired() { + return Collections.unmodifiableList(required); + } + + /** @return unmodifiable list of required library files, guaranteed readable */ + public List getLibJars() { + return Collections.unmodifiableList(libJars); + } + + /** @return unmodifiable list of source directories, guaranteed readable */ + public List getSrcDirs() { + return Collections.unmodifiableList(srcDirs); + } + + /** @return Modules registry of known modules, including this one */ + public Modules getModules() { + return modules; + } + + /** @return List of File jar paths to be merged into module-dist */ + public List getMerges() { + String value = properties.getProperty(name + ".merges"); + if ((null == value) || (0 == value.length())) { + return Collections.EMPTY_LIST; + } + ArrayList result = new ArrayList(); + StringTokenizer st = new StringTokenizer(value); + while (st.hasMoreTokens()) { + result.addAll(findJarsBySuffix(st.nextToken())); + } + return result; + } + + + public void clearOutOfDate() { + outOfDate = false; + outOfDateSet = false; + } + + /** + * @param recalculate if true, then force recalculation + * @return true if the target jar for this module is older than + * any source files in a source directory + * or any required modules + * or any libraries + * or if any libraries or required modules are missing + */ + public boolean outOfDate(boolean recalculate) { + if (recalculate) { + outOfDateSet = false; + } + if (!outOfDateSet) { + outOfDate = false; + try { + if (!(moduleJar.exists() && moduleJar.canRead())) { + return outOfDate = true; + } + final long time = moduleJar.lastModified(); + File file; + for (Iterator iter = srcDirs.iterator(); iter.hasNext();) { + File srcDir = (File) iter.next(); + for (Iterator srcFiles = sourceFiles(srcDir); srcFiles.hasNext();) { + file = (File) srcFiles.next(); + if (outOfDate(time, file)) { + return outOfDate = true; + } + } + } + // required modules + for (Iterator iter = getRequired().iterator(); iter.hasNext();) { + Module required = (Module) iter.next(); + file = required.getModuleJar(); + if (outOfDate(time, file)) { + return outOfDate = true; + } + } + // libraries + for (Iterator iter = getLibJars().iterator(); iter.hasNext();) { + file = (File) iter.next(); + if (outOfDate(time, file)) { + return outOfDate = true; + } + } + } finally { + outOfDateSet = true; + } + } + return outOfDate; + } + /** + * Add any (File) library jar or (File) required module jar + * to the List known, if not added already. + */ + public ArrayList findKnownJarAntecedants() { + ArrayList result = new ArrayList(); + doFindKnownJarAntecedants(this, result); + return result; + } + + public String toString() { + return name; + } + + public String toLongString() { + return + "Module [name=" + + name + + ", srcDirs=" + + srcDirs + + ", required=" + + required + + ", moduleJar=" + + moduleJar + + ", libJars=" + + libJars + + "]"; + } + + private boolean init() { + return initClasspath() && initProperties() && reviewInit(); + } + + /** read eclipse .classpath file XXX line-oriented hack */ + private boolean initClasspath() { + // meaning testsrc directory, junit library, etc. + File file = new File(moduleDir, ".classpath"); // XXXFileLiteral + FileReader fin = null; + try { + fin = new FileReader(file); + BufferedReader reader = new BufferedReader(fin); + String line; + String lastKind = null; + while (null != (line = reader.readLine())) { + lastKind = parseLine(line, lastKind); + } + return (0 < srcDirs.size()); + } catch (IOException e) { + messager.logException("IOException reading " + file, e); + } finally { + if (null != fin) { + try { fin.close(); } + catch (IOException e) {} // ignore + } + } + return false; + } + + /** @return true if any properties were read correctly */ + private boolean initProperties() { + File file = new File(moduleDir, name + ".properties"); // XXXFileLiteral + if (!Util.canReadFile(file)) { + return true; // no properties to read + } + FileInputStream fin = null; + try { + fin = new FileInputStream(file); + properties.load(fin); + return true; + } catch (IOException e) { + messager.logException("IOException reading " + file, e); + return false; + } finally { + if (null != fin) { + try { fin.close(); } + catch (IOException e) {} // ignore + } + } + } + + /** + * Post-process initialization. + * This implementation trims testing-related source + * directories, libraries, and modules if trimTesting is enabled/true. + * To build testing modules, trimTesting must be false. + * @return true if initialization post-processing worked + */ + protected boolean reviewInit() { + if (!trimTesting) { + return true; + } + try { + for (ListIterator iter = srcDirs.listIterator(); iter.hasNext();) { + File srcDir = (File) iter.next(); + String name = srcDir.getName(); + if ("testsrc".equals(name.toLowerCase())) { // XXXFileLiteral + iter.remove(); // XXX if verbose log + } + } + for (ListIterator iter = libJars.listIterator(); iter.hasNext();) { + File libJar = (File) iter.next(); + String name = libJar.getName(); + if ("junit.jar".equals(name.toLowerCase())) { // XXXFileLiteral + iter.remove(); // XXX if verbose log + } + } + for (ListIterator iter = required.listIterator(); iter.hasNext();) { + Module required = (Module) iter.next(); + String name = required.name; + // XXX testing-util only ? + if (name.toLowerCase().startsWith("testing")) { // XXXFileLiteral + iter.remove(); // XXX if verbose log + } + } + } catch (UnsupportedOperationException e) { + return false; // failed XXX log also if verbose + } + return true; + } + + private String parseLine(String line, String lastKind) { + if (null == line) { + return null; + } + String kind; + int loc = line.indexOf("kind=\""); + if ((-1 == loc) || (loc + 9 > line.length())) { + // no kind string - fail unless have lastKind + if (null == lastKind) { + return null; + } else { + kind = lastKind; + } + } else { // have kind string - get kind + loc += 6; // past kind=" + kind = line.substring(loc, loc+3); + } + + // now look for value + loc = line.indexOf("path=\""); + if (-1 == loc) { // no value - return lastKind + return kind; + } + loc += 6; // past path=" + int end = line.indexOf("\"", loc); + if (-1 == end) { + throw new Error("unterminated path in " + line); + } + final String path = line.substring(loc, end); + + if ("src".equals(kind)) { + if (path.startsWith("/")) { // module + String moduleName = path.substring(1); + Module req = modules.getModule(moduleName); + if (null != req) { + required.add(req); + } else { + messager.error("unable to create required module: " + moduleName); + } + } else { // src dir + File srcDir = new File(moduleDir, path); + if (srcDir.canRead() && srcDir.isDirectory()) { + srcDirs.add(srcDir); + } else { + messager.error("not a src dir: " + srcDir); + } + } + } else if ("lib".equals(kind)) { + String libPath = path.startsWith("/") + ? modules.baseDir.getAbsolutePath() + path + : path; + File libJar = new File(libPath); + if (libJar.canRead() && libJar.isFile()) { + libJars.add(libJar); + } else { + messager.error("no such library jar " + libJar + " from " + line); + } + } else if ("var".equals(kind)) { + if (!"JRE_LIB".equals(path)) { + messager.log("cannot handle var yet: " + line); + } + } else if ("con".equals(kind)) { + messager.log("cannot handle con yet: " + line); + } else if ("out".equals(kind)) { + // ignore output entries + } else { + messager.log("unrecognized kind " + kind + " in " + line); + } + return null; + } + + /** @return List of File of any module or library jar ending with suffix */ + private ArrayList findJarsBySuffix(String suffix) { + ArrayList result = new ArrayList(); + if (null != suffix) { + // library jars + for (Iterator iter = getLibJars().iterator(); iter.hasNext();) { + File file = (File) iter.next(); + if (file.getPath().endsWith(suffix)) { + result.add(file); + } + } + // module jars + for (Iterator iter = getRequired().iterator(); iter.hasNext();) { + Module module = (Module) iter.next(); + File file = module.getModuleJar(); + if (file.getPath().endsWith(suffix)) { + result.add(file); + } + } + } + return result; + } +} + diff --git a/build/src/org/aspectj/internal/tools/build/Modules.java b/build/src/org/aspectj/internal/tools/build/Modules.java new file mode 100644 index 000000000..55b2fa800 --- /dev/null +++ b/build/src/org/aspectj/internal/tools/build/Modules.java @@ -0,0 +1,68 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.internal.tools.build; + +import java.io.File; +import java.util.Hashtable; + +/** + * Registration and factory for modules + * @see Module + * @see Builder + */ +public class Modules { + + private final Hashtable modules = new Hashtable(); + public final File baseDir; + public final File jarDir; + private final Messager handler; + public final boolean trimTesting; + + public Modules(File baseDir, File jarDir, boolean trimTesting, Messager handler) { + this.baseDir = baseDir; + this.jarDir = jarDir; + this.handler = handler; + this.trimTesting = trimTesting; + Util.iaxIfNotCanReadDir(baseDir, "baseDir"); + Util.iaxIfNotCanReadDir(jarDir, "jarDir"); + Util.iaxIfNull(handler, "handler"); + } + + + /** + * Get module associated with name. + * @return fail if unable to find or create module {name}. + */ + public Module getModule(String name) { + if (null == name) { + return null; + } + Module result = (Module) modules.get(name); + if (null == result) { + File moduleDir = new File(baseDir, name); + if (!Util.canReadDir(moduleDir)) { + handler.error("not a module: " + name); + } else { + result = new Module(moduleDir, jarDir, name, this, trimTesting, handler); + if (result.valid) { + modules.put(name, result); + } else { + handler.error("invalid module: " + result.toLongString()); + } + } + } + return result; + } +}
\ No newline at end of file diff --git a/build/src/org/aspectj/internal/tools/build/ProductModule.java b/build/src/org/aspectj/internal/tools/build/ProductModule.java new file mode 100644 index 000000000..90e6f3879 --- /dev/null +++ b/build/src/org/aspectj/internal/tools/build/ProductModule.java @@ -0,0 +1,70 @@ +/* ******************************************************************* + * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ +package org.aspectj.internal.tools.build; + +import java.io.File; + +/** + * Struct associating module with target product distribution jar + * and assembly instructions. + * When building product distributions, a zero-length jar file + * in the dist directory may signify a module to be built, + * renamed, and included in the distribution. + */ +public class ProductModule { + /** name of distribution directory in product directory */ + private static final String DIST = "dist"; + + /** top-level product directory being produced */ + public final File productDir; + + /** path to file in distribution template dir for this module jar */ + public final File replaceFile; + + /** relative path within distribution of this product module jar */ + public final String relativePath; + + /** the module jar is the file to replace */ + public final Module module; + + /** if true, assemble all when building module */ + public final boolean assembleAll; + + public ProductModule(File productDir, File replaceFile, Module module, boolean assembleAll) { + this.replaceFile = replaceFile; + this.module = module; + this.productDir = productDir; + this.assembleAll = assembleAll; + Util.iaxIfNull(module, "module"); + Util.iaxIfNotCanReadDir(productDir, "productDir"); + Util.iaxIfNotCanReadFile(replaceFile, "replaceFile"); + String productDirPath = productDir.getAbsolutePath(); + String replaceFilePath = replaceFile.getAbsolutePath(); + if (!replaceFilePath.startsWith(productDirPath)) { + String m = "\"" + replaceFilePath + + "\" does not start with \"" + + productDirPath + + "\""; + throw new IllegalArgumentException(m); + } + replaceFilePath = replaceFilePath.substring(1+productDirPath.length()); + if (!replaceFilePath.startsWith(DIST)) { + String m = "\"" + replaceFilePath + + "\" does not start with \"" + DIST + "\""; + throw new IllegalArgumentException(m); + } + relativePath = replaceFilePath.substring(1 + DIST.length()); + } + public String toString() { + return "" + module + " for " + productDir; + } +} diff --git a/build/src/org/aspectj/internal/tools/build/Util.java b/build/src/org/aspectj/internal/tools/build/Util.java new file mode 100644 index 000000000..27ca9e1b4 --- /dev/null +++ b/build/src/org/aspectj/internal/tools/build/Util.java @@ -0,0 +1,174 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.internal.tools.build; + +import java.io.File; +import java.io.FileFilter; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; + +/** + * Build-only utilities. + * Many mirror utils module APIs. + */ +public class Util { + + /** + * Map version in long form to short, + * e.g., replacing "alpha" with "a" + */ + public static String shortVersion(String version) { + version = Util.replace(version, "alpha", "a"); + version = Util.replace(version, "beta", "b"); + version = Util.replace(version, "candidate", "rc"); + version = Util.replace(version, "development", "d"); + version = Util.replace(version, "dev", "d"); + return version; + } + + /** + * Replace any instances of {replace} in {input} with {with}. + * @param input the String to search/replace + * @param replace the String to search for in input + * @param with the String to replace with in input + * @return input if it has no replace, otherwise a new String + */ + public static String replace(String input, String replace, String with) { + int loc = input.indexOf(replace); + if (-1 != loc) { + String result = input.substring(0, loc); + result += with; + int start = loc + replace.length(); + if (start < input.length()) { + result += input.substring(start); + } + input = result; + } + return input; + } + + /** @return false if filter returned false for any file in baseDir subtree */ + public static boolean visitFiles(File baseDir, FileFilter filter) { + Util.iaxIfNotCanReadDir(baseDir, "baseDir"); + Util.iaxIfNull(filter, "filter"); + File[] files = baseDir.listFiles(); + boolean passed = true; + for (int i = 0; passed && (i < files.length); i++) { + passed = files[i].isDirectory() + ? visitFiles(files[i], filter) + : filter.accept(files[i]); + } + return passed; + } + + /** @throws IllegalArgumentException if cannot read dir */ + public static void iaxIfNotCanReadDir(File dir, String name) { + if (!canReadDir(dir)) { + throw new IllegalArgumentException(name + " dir not readable: " + dir); + } + } + + /** @throws IllegalArgumentException if cannot read file */ + public static void iaxIfNotCanReadFile(File file, String name) { + if (!canReadFile(file)) { + throw new IllegalArgumentException(name + " file not readable: " + file); + } + } + + /** @throws IllegalArgumentException if cannot write dir */ + public static void iaxIfNotCanWriteDir(File dir, String name) { + if (!canWriteDir(dir)) { + throw new IllegalArgumentException(name + " dir not writeable: " + dir); + } + } + + /** @throws IllegalArgumentException if input is null */ + public static void iaxIfNull(Object input, String name) { + if (null == input) { + throw new IllegalArgumentException("null " + name); + } + } + + /** render exception to String */ + public static String renderException(Throwable thrown) { + if (null == thrown) { + return "(Throwable) null"; + } + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw, true); + pw.println(thrown.getMessage()); + thrown.printStackTrace(pw); + pw.flush(); + return sw.getBuffer().toString(); + } + + /** @return true if dir is a writable directory */ + public static boolean canWriteDir(File dir) { + return (null != dir) && dir.canWrite() && dir.isDirectory(); + } + + /** @return true if dir is a readable directory */ + public static boolean canReadDir(File dir) { + return (null != dir) && dir.canRead() && dir.isDirectory(); + } + + /** @return true if dir is a readable file */ + public static boolean canReadFile(File file) { + return (null != file) && file.canRead() && file.isFile(); + } + + /** + * Delete contents of directory. + * The directory itself is not deleted. + * @param dir the File directory whose contents should be deleted. + * @return true if all contents of dir were deleted + */ + public static boolean deleteContents(File dir) { + if ((null == dir) || !dir.canWrite()) { + return false; + } else if (dir.isDirectory()) { + File[] files = dir.listFiles(); + for (int i = 0; i < files.length; i++) { + if (!deleteContents(files[i]) || !files[i].delete()) { + return false; + } + } + } + return true; + } + + /** @return File temporary directory with the given prefix */ + public static File makeTempDir(String prefix) { + if (null == prefix) { + prefix = "tempDir"; + } + File tempFile = null; + for (int i = 0; i < 10; i++) { + try { + tempFile = File.createTempFile(prefix,"tmp"); + tempFile.delete(); + if (tempFile.mkdirs()) { + break; + } + tempFile = null; + } catch (IOException e) { + } + } + return tempFile; + } + +} + diff --git a/build/src/org/aspectj/internal/tools/build/package.html b/build/src/org/aspectj/internal/tools/build/package.html new file mode 100644 index 000000000..3fa443812 --- /dev/null +++ b/build/src/org/aspectj/internal/tools/build/package.html @@ -0,0 +1,12 @@ +<html> +<!-- todo +- backed off on doing product installer builds directly; + the one installer is now built using Ant. + +- +--> +<body> +The build taskdef relies on the classes in this package for +behavior independent of Ant. +</body> +</html> diff --git a/build/testsrc/BuildModuleTests.java b/build/testsrc/BuildModuleTests.java new file mode 100644 index 000000000..651b061a6 --- /dev/null +++ b/build/testsrc/BuildModuleTests.java @@ -0,0 +1,143 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + + +// default package + +import org.aspectj.internal.tools.ant.taskdefs.Checklics; +import org.aspectj.internal.build.BuildModuleTest; + +import java.io.File; + +import junit.framework.*; + +public class BuildModuleTests extends TestCase { + + /** if true, then replace old headers with new first */ + private static final boolean replacing = false; // XXX never to enable again... + + /** if any replace failed, halt all */ + private static boolean replaceFailed = false; + + private static final String BASE_DIR = "../"; + private static final String[] JDT_SOURCE_DIRS = new String[] + {"antadapter", "batch", "codeassist", "compiler", "dom", "eval", "formatter", + "model", "search" }; + + public static Test suite() { + TestSuite suite = new TestSuite("Build module tests"); + suite.addTestSuite(BuildModuleTests.class); + suite.addTestSuite(BuildModuleTest.class); + return suite; + } + + /** @return String tag of license if not default */ + public static String getLicense(String module) { + if ("org.eclipse.jdt.core".equals(module)) { + return Checklics.CPL_IBM_PARC_TAG; + } + return null; + } + + public BuildModuleTests(String name) { super(name); } + + public void testLicense_ajbrowser() { + checkLicense("ajbrowser"); + } + public void testLicense_ajde() { + checkLicense("ajde"); + } + public void testLicense_asm() { + checkLicense("asm"); + } + public void testLicense_bcweaver() { + String module = "bcweaver"; + checkSourceDirectory("../" + module + "/src", module); + checkSourceDirectory("../" + module + "/testsrc/org", module); + } + public void testLicense_bridge() { + checkLicense("bridge"); + } + public void testLicense_build() { + checkLicense("build"); + } + public void testLicense_jbuilder() { + checkLicense("jbuilder"); + } + public void testLicense_netbeans() { + checkLicense("netbeans"); + } + public void testLicense_org_aspectj_ajdt_core() { + checkLicense("org.aspectj.ajdt.core"); + } + public void testLicense_org_eclipse_jdt_core() { + final String mod = "org.eclipse.jdt.core"; + final String pre = BASE_DIR + mod + "/"; + for (int i = 0; i < JDT_SOURCE_DIRS.length; i++) { + checkSourceDirectory(pre + JDT_SOURCE_DIRS[i], mod); + } + } + + public void testLicense_runtime() { + checkLicense("runtime"); + } + public void testLicense_taskdefs() { + checkLicense("taskdefs"); + } + public void testLicense_testing() { + checkLicense("testing"); + } + public void testLicense_testing_drivers() { + checkLicense("testing-drivers"); + } + public void testLicense_testing_client() { + checkLicense("testing-client"); + } + public void testLicense_testing_util() { + checkLicense("testing-util"); + } + public void testLicense_util() { + checkLicense("util"); + } + + void checkLicense(String module) { + checkSourceDirectory("../" + module + "/src", module); + checkSourceDirectory("../" + module + "/testsrc", module); + } + + void checkSourceDirectory(String path, String module) { + File moduleDir = new File(path); + final String label = "source dir " + moduleDir + " (module " + module + ")"; + assertTrue(label, (moduleDir.exists() && moduleDir.isDirectory())); + String license = getLicense(module); + if (replacing) { + if (replacing) { + throw new Error("replacing done - code left for other replaces"); + } + assertTrue("aborting - replace failed", !replaceFailed); + // do the replace + int fails = Checklics.runDirect(moduleDir.getPath(), "replace-headers"); + replaceFailed = (0 != fails); + assertTrue(!replaceFailed); + license = Checklics.CPL_IBM_PARC_XEROX_TAG; + } + int fails = Checklics.runDirect(moduleDir.getPath(), license); + if (0 != fails) { + if (replacing) { + replaceFailed = true; + } + assertTrue(label + " fails", false); + } + } + +} diff --git a/build/testsrc/org/aspectj/internal/build/BuildModuleTest.java b/build/testsrc/org/aspectj/internal/build/BuildModuleTest.java new file mode 100644 index 000000000..6bee0a63f --- /dev/null +++ b/build/testsrc/org/aspectj/internal/build/BuildModuleTest.java @@ -0,0 +1,179 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + +package org.aspectj.internal.build; + +import org.apache.tools.ant.Project; +import org.apache.tools.ant.taskdefs.Java; +import org.apache.tools.ant.types.Path; +import org.apache.tools.ant.types.Commandline.Argument; +import org.aspectj.internal.tools.ant.taskdefs.BuildModule; + +import java.io.File; +import java.util.ArrayList; +import java.util.Iterator; + +import junit.framework.TestCase; +/** + * + */ +public class BuildModuleTest extends TestCase { + + private static final String SKIP_MESSAGE = + "Define \"run.build.tests\" as a system property to run tests to build "; + private static boolean delete(File file) { // XXX Util + if ((null == file) || !file.exists()) { + return true; + } + if (file.isFile()) { + return file.delete(); + } else { + File[] files = file.listFiles(); + boolean result = true; + for (int i = 0; i < files.length; i++) { + if (!BuildModuleTest.delete(files[i]) + && result) { + result = false; + } + } + return (file.delete() && result); + } + } + + ArrayList tempFiles = new ArrayList(); + boolean building; // must be enabled for tests to run + + public BuildModuleTest(String name) { + super(name); + building = (null != System.getProperty("run.build.tests")); + } + + protected void tearDown() throws Exception { + super.tearDown(); + for (Iterator iter = tempFiles.iterator(); iter.hasNext();) { + File file = (File) iter.next(); + if (!BuildModuleTest.delete(file)) { + System.err.println("warning: BuildModuleTest unable to delete " + file); + } + } + } + + public void testBuild() { + checkBuild("build", null, null); + } + public void testAsm() { + checkBuild("asm", null, null); + } + + public void testRuntime() { + checkBuild("runtime", null, null); + } + + public void testAjbrowser() { + checkBuild("ajbrowser", null, null); + } + + public void testAjdt() { + checkBuild("org.aspectj.ajdt.core", "org.aspectj.tools.ajc.Main", + new String[] { "-version" }); + } + + public void testAspectjtools() { + if (!building) { + System.err.println(SKIP_MESSAGE + "aspectjtools"); + return; + } + File baseDir = new File(".."); + File tempBuildDir = new File(baseDir, "aj-build"); + File distDir = new File(tempBuildDir, "dist"); + File jarDir = new File(tempBuildDir, "jars"); + assertTrue(distDir.canWrite() || distDir.mkdirs()); + File productDir = new File(baseDir.getPath() + "/build/products/tools"); + assertTrue(""+productDir, productDir.canRead()); + checkBuildProduct(productDir, baseDir, distDir, jarDir); + } + + void checkBuildProduct(File productDir, File baseDir, File distDir, File jarDir) { + if (!building) { + System.err.println(SKIP_MESSAGE + "product " + productDir); + return; + } + assertTrue(null != productDir); + assertTrue(productDir.canRead()); + + checkJavac(); + + BuildModule task = new BuildModule(); + Project project = new Project(); + task.setProject(project); + assertTrue(jarDir.canWrite() || jarDir.mkdirs()); + // XXX restore tempFiles.add(jarDir); + task.setJardir(new Path(project, jarDir.getAbsolutePath())); + task.setProductdir(new Path(project, productDir.getAbsolutePath())); + task.setBasedir(new Path(project, baseDir.getAbsolutePath())); + task.setDistdir(new Path(project, distDir.getAbsolutePath())); + task.setFailonerror(true); + //task.setVerbose(true); + task.setCreateinstaller(true); + task.execute(); + // now run installer and do product tests? + } + + void checkBuild(String module, String classname, String[] args) { + if (!building) { + System.err.println(SKIP_MESSAGE + "module " + module); + return; + } + assertTrue(null != module); + checkJavac(); + + BuildModule task = new BuildModule(); + Project project = new Project(); + task.setProject(project); + File jarDir = new File("tempJarDir"); + assertTrue(jarDir.canWrite() || jarDir.mkdirs()); + tempFiles.add(jarDir); + task.setModuledir(new Path(project, "../" + module)); + task.setJardir(new Path(project, jarDir.getAbsolutePath())); + task.setFailonerror(true); + task.execute(); + + if (null == classname) { + return; + } + + Java java = new Java(); + java.setProject(project); + File jar = new File(jarDir, module + ".jar"); + java.setClasspath(new Path(project, jar.getAbsolutePath())); + java.setClassname(classname); + for (int i = 0; i < args.length; i++) { + Argument arg = java.createArg(); + arg.setValue(args[i]); + } + java.execute(); + } + + void checkJavac() { + boolean result = false; + try { + result = (null != Class.forName("sun.tools.javac.Main")); + } catch (Throwable t) { + // ignore + } + if (! result) { + assertTrue("add tools.jar to the classpath for Ant's use of javac", false); + } + } + +} diff --git a/runtime/src/.cvsignore b/runtime/src/.cvsignore new file mode 100644 index 000000000..a3f0b1b77 --- /dev/null +++ b/runtime/src/.cvsignore @@ -0,0 +1 @@ +*.lst diff --git a/runtime/src/org/aspectj/lang/JoinPoint.java b/runtime/src/org/aspectj/lang/JoinPoint.java new file mode 100644 index 000000000..4fae1571a --- /dev/null +++ b/runtime/src/org/aspectj/lang/JoinPoint.java @@ -0,0 +1,179 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.lang; + +import org.aspectj.lang.reflect.SourceLocation; + +/** + * <p>Provides reflective access to both the state available at a join point and + * static information about it. This information is available from the body + * of advice using the special form <code>thisJoinPoint</code>. The primary + * use of this reflective information is for tracing and logging applications. + * </p> + * + * <pre> + * aspect Logging { + * before(): within(com.bigboxco..*) && execution(public * *(..)) { + * System.err.println("entering: " + thisJoinPoint); + * System.err.println(" w/args: " + thisJoinPoint.getArgs()); + * System.err.println(" at: " + thisJoinPoint.getSourceLocation()); + * } + * } + * </pre> + */ +public interface JoinPoint { + + String toString(); + + /** + * Returns an abbreviated string representation of the join point. + */ + String toShortString(); + + /** + * Returns an extended string representation of the join point. + */ + String toLongString(); + + /** + * <p> Returns the currently executing object. This will always be + * the same object as that matched by the <code>this</code> pointcut + * designator. Unless you specifically need this reflective access, + * you should use the <code>this</code> pointcut designator to + * get at this object for better static typing and performance.</p> + * + * <p> Returns null when there is no currently executing object available. + * This includes all join points that occur in a static context.</p> + */ + Object getThis(); + + /** + * <p> Returns the target object. This will always be + * the same object as that matched by the <code>target</code> pointcut + * designator. Unless you specifically need this reflective access, + * you should use the <code>target</code> pointcut designator to + * get at this object for better static typing and performance.</p> + * + * <p> Returns null when there is no target object.</p> + + */ + Object getTarget(); + + /** + * <p>Returns the arguments at this join point.</p> + */ + Object[] getArgs(); + + /** Returns the signature at the join point. + * + * <code>getStaticPart().getSignature()</code> returns the same object + */ + Signature getSignature(); + + /** <p>Returns the source location corresponding to the join point.</p> + * + * <p>If there is no source location available, returns null.</p> + * + * <p>Returns the SourceLocation of the defining class for default constructors.</p> + * + * <p> <code>getStaticPart().getSourceLocation()</code> returns the same object. </p> + */ + SourceLocation getSourceLocation(); + + /** Returns a String representing the kind of join point. This + * String is guaranteed to be + * interned. <code>getStaticPart().getKind()</code> returns + * the same object. + */ + String getKind(); + + + /** + * <p>This helper object contains only the static information about a join point. + * It is available from the <code>JoinPoint.getStaticPart()</code> method, and + * can be accessed separately within advice using the special form + * <code>thisJoinPointStaticPart</code>.</p> + * + * <p>If you are only interested in the static information about a join point, + * you should access it through this type for the best performance. This + * is particularly useful for library methods that want to do serious + * manipulations of this information, i.e.</p> + * + * <pre> + * public class LoggingUtils { + * public static void prettyPrint(JoinPoint.StaticPart jp) { + * ... + * } + * } + * + * aspect Logging { + * before(): ... { LoggingUtils.prettyPrint(thisJoinPointStaticPart); } + * } + * </pre> + * + * @see JoinPoint#getStaticPart() + */ + public interface StaticPart { + /** Returns the signature at the join point. */ + Signature getSignature(); + + /** <p>Returns the source location corresponding to the join point.</p> + * + * <p>If there is no source location available, returns null.</p> + * + * <p>Returns the SourceLocation of the defining class for default constructors.</p> + */ + SourceLocation getSourceLocation(); + + /** <p> Returns a String representing the kind of join point. This String + * is guaranteed to be interned</p> + */ + String getKind(); + + String toString(); + + /** + * Returns an abbreviated string representation of the join point + */ + String toShortString(); + + /** + * Returns an extended string representation of the join point + */ + String toLongString(); + } + + + /** <p> Returns an object that encapsulates the static parts of this join point </p> + */ + StaticPart getStaticPart(); + + + /** + * The legal return values from getKind() + */ + static String METHOD_EXECUTION = "method-execution"; + static String METHOD_CALL = "method-call"; + static String CONSTRUCTOR_EXECUTION = "constructor-execution"; + static String CONSTRUCTOR_CALL = "constructor-call"; + static String FIELD_GET = "field-get"; + static String FIELD_SET = "field-set"; + static String STATICINITIALIZATION = "staticinitialization"; + static String PREINTIALIZATION = "preinitialization"; + static String INITIALIZATION = "initialization"; + static String EXCEPTION_HANDLER = "exception-handler"; + + static String ADVICE_EXECUTION = "advice-execution"; //??? consider this vs. pcd +} diff --git a/runtime/src/org/aspectj/lang/NoAspectBoundException.java b/runtime/src/org/aspectj/lang/NoAspectBoundException.java new file mode 100644 index 000000000..4bebcfb47 --- /dev/null +++ b/runtime/src/org/aspectj/lang/NoAspectBoundException.java @@ -0,0 +1,21 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.lang; + +/** Thrown by the <code>aspectOf(..)</code> special method on aspect types + * when there is no aspect of that type currently bound. + */ +public class NoAspectBoundException extends RuntimeException { +} diff --git a/runtime/src/org/aspectj/lang/Signature.java b/runtime/src/org/aspectj/lang/Signature.java new file mode 100644 index 000000000..91e2f00a2 --- /dev/null +++ b/runtime/src/org/aspectj/lang/Signature.java @@ -0,0 +1,95 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.lang; + +/** <p>Represents the signature at a join point. This interface parallels + * <code>java.lang.reflect.Member</code>. </p> + * + * <p>This interface is typically used for tracing or logging applications + * to obtain reflective information about the join point, i.e. using + * the j2se 1.4 <code>java.util.logging</code> API</p> + * <pre> + * aspect Logging { + * Logger logger = Logger.getLogger("MethodEntries"); + * + * before(): within(com.bigboxco..*) && execution(public * *(..)) { + * Signature sig = thisJoinPoint.getSignature(); + * logger.entering(sig.getDeclaringType().getName(), + * sig.getName()); + * } + * } + * </pre> + * + * + * <p>More detailed information about a specific kind of signature can + * be obtained by casting this <code>Signature</code> object into one + * of its more specific sub-types available in + * <code>org.aspectj.lang.reflect</code>. + * + * @see java.lang.reflect.Member + * @see java.util.logging.Logger + */ +public interface Signature { + String toString(); + /** + * Returns an abbreviated string representation of this signature. + */ + String toShortString(); + + /** + * Returns an extended string representation of this signature. + */ + String toLongString(); + + + /** + * Returns the identifier part of this signature; i.e. for methods this + * will return the method name. + * + * @see java.lang.reflect.Member#getName + */ + String getName(); + + /** + * Returns the modifiers on this signature represented as an int. Use + * the constants and helper methods defined on + * <code>java.lang.reflect.Modifier</code> to manipulate this, i.e. + * <pre> + * // check if this signature is public + * java.lang.reflect.Modifier.isPublic(sig.getModifiers()); + * + * // print out the modifiers + * java.lang.reflect.Modifier.toString(sig.getModifiers()); + * </pre> + * + * @see java.lang.reflect.Member#getModifiers + * @see java.lang.reflect.Modifier + */ + int getModifiers(); + + /** + * <p>Returns a <code>java.lang.Class</code> object representing the class, + * interface, or aspect that declared this member. For intra-member + * declarations, this will be the type on which the member is declared, + * not the type where the declaration is lexically written. Use + * <code>SourceLocation.getWithinType()</code> to get the type in + * which the declaration occurs lexically.</p> + * <p>For consistency with <code>java.lang.reflect.Member</code>, this + * method should have been named <code>getDeclaringClass()</code>.</p> + * + * @see java.lang.reflect.Member#getDeclaringClass + */ + Class getDeclaringType(); +} diff --git a/runtime/src/org/aspectj/lang/SoftException.java b/runtime/src/org/aspectj/lang/SoftException.java new file mode 100644 index 000000000..e3c19ed80 --- /dev/null +++ b/runtime/src/org/aspectj/lang/SoftException.java @@ -0,0 +1,37 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.lang; + +/** + * Wrapper for checked exceptions matched by a 'declare soft'. + * You can soften checked exceptions at join points by using + * the form <code>declare soft: TypePattern: Pointcut</code>. + * At the join points, any exceptions thrown which match + * TypePattern will be wrapped in <code>SoftException</code> + * and rethrown. You can get the original exception using + * <code>getWrappedThrowable()</code>. + */ +public class SoftException extends RuntimeException { + Throwable inner; + public SoftException(Throwable inner) { + super(); + this.inner = inner; + } + + public Throwable getWrappedThrowable() { return inner; } + + //XXX should add a getCause() method to parallel j2se 1.4's new + //XXX chained exception mechanism +} diff --git a/runtime/src/org/aspectj/lang/package.html b/runtime/src/org/aspectj/lang/package.html new file mode 100644 index 000000000..a5fe7ae5d --- /dev/null +++ b/runtime/src/org/aspectj/lang/package.html @@ -0,0 +1,14 @@ +<html> +<body> +Provides several interfaces for obtaining reflective information about a +join point, as well as several exceptions that can be thrown by AspectJ +code. +<p> +<code>JoinPoint</code> and <code>Signature</code> provide reflective +information about a join point. Instances of these interfaces are +available inside of <code>advice</code> with the special variables +<code>thisJoinPoint</code>, <code>thisJoinPointStaticPart</code>, and +<code>thisEnclosingJoinPointStaticPart</code>.</p> + +</body> +</html> diff --git a/runtime/src/org/aspectj/lang/reflect/AdviceSignature.java b/runtime/src/org/aspectj/lang/reflect/AdviceSignature.java new file mode 100644 index 000000000..8afc4e1ac --- /dev/null +++ b/runtime/src/org/aspectj/lang/reflect/AdviceSignature.java @@ -0,0 +1,21 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.lang.reflect; + +public interface AdviceSignature extends CodeSignature { + Class getReturnType(); /* name is consistent with reflection API */ + /* before and after always return Void.TYPE */ + /* (some around also return Void.Type) */ +} diff --git a/runtime/src/org/aspectj/lang/reflect/CatchClauseSignature.java b/runtime/src/org/aspectj/lang/reflect/CatchClauseSignature.java new file mode 100644 index 000000000..8932395aa --- /dev/null +++ b/runtime/src/org/aspectj/lang/reflect/CatchClauseSignature.java @@ -0,0 +1,22 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.lang.reflect; + +import org.aspectj.lang.Signature; + +public interface CatchClauseSignature extends Signature { + Class getParameterType(); + String getParameterName(); +} diff --git a/runtime/src/org/aspectj/lang/reflect/CodeSignature.java b/runtime/src/org/aspectj/lang/reflect/CodeSignature.java new file mode 100644 index 000000000..795cb6dc9 --- /dev/null +++ b/runtime/src/org/aspectj/lang/reflect/CodeSignature.java @@ -0,0 +1,21 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.lang.reflect; + +public interface CodeSignature extends MemberSignature { + Class[] getParameterTypes(); + String[] getParameterNames(); + Class[] getExceptionTypes(); +} diff --git a/runtime/src/org/aspectj/lang/reflect/ConstructorSignature.java b/runtime/src/org/aspectj/lang/reflect/ConstructorSignature.java new file mode 100644 index 000000000..66ab51397 --- /dev/null +++ b/runtime/src/org/aspectj/lang/reflect/ConstructorSignature.java @@ -0,0 +1,18 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.lang.reflect; + +public interface ConstructorSignature extends CodeSignature { +} diff --git a/runtime/src/org/aspectj/lang/reflect/FieldSignature.java b/runtime/src/org/aspectj/lang/reflect/FieldSignature.java new file mode 100644 index 000000000..0983509c5 --- /dev/null +++ b/runtime/src/org/aspectj/lang/reflect/FieldSignature.java @@ -0,0 +1,21 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.lang.reflect; + +import org.aspectj.lang.Signature; + +public interface FieldSignature extends MemberSignature { + public Class getFieldType(); +} diff --git a/runtime/src/org/aspectj/lang/reflect/InitializerSignature.java b/runtime/src/org/aspectj/lang/reflect/InitializerSignature.java new file mode 100644 index 000000000..2b75f92a6 --- /dev/null +++ b/runtime/src/org/aspectj/lang/reflect/InitializerSignature.java @@ -0,0 +1,17 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.lang.reflect; + +public interface InitializerSignature extends CodeSignature { } diff --git a/runtime/src/org/aspectj/lang/reflect/MemberSignature.java b/runtime/src/org/aspectj/lang/reflect/MemberSignature.java new file mode 100644 index 000000000..31f21df7d --- /dev/null +++ b/runtime/src/org/aspectj/lang/reflect/MemberSignature.java @@ -0,0 +1,20 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.lang.reflect; + +import org.aspectj.lang.Signature; + +public interface MemberSignature extends Signature { +} diff --git a/runtime/src/org/aspectj/lang/reflect/MethodSignature.java b/runtime/src/org/aspectj/lang/reflect/MethodSignature.java new file mode 100644 index 000000000..f26d6c504 --- /dev/null +++ b/runtime/src/org/aspectj/lang/reflect/MethodSignature.java @@ -0,0 +1,19 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.lang.reflect; + +public interface MethodSignature extends CodeSignature { + Class getReturnType(); /* name is consistent with reflection API */ +} diff --git a/runtime/src/org/aspectj/lang/reflect/SourceLocation.java b/runtime/src/org/aspectj/lang/reflect/SourceLocation.java new file mode 100644 index 000000000..cb6bfcf95 --- /dev/null +++ b/runtime/src/org/aspectj/lang/reflect/SourceLocation.java @@ -0,0 +1,30 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.lang.reflect; + +/** For defining code, the class defined and location in a source file. */ +public interface SourceLocation { + Class getWithinType(); + + String getFileName(); + int getLine(); + + /** + * @deprecated can not be implemented for bytecode weaving, may + * be removed in 1.1gold. + */ + int getColumn(); +} + diff --git a/runtime/src/org/aspectj/lang/reflect/package.html b/runtime/src/org/aspectj/lang/reflect/package.html new file mode 100644 index 000000000..fc51a5b60 --- /dev/null +++ b/runtime/src/org/aspectj/lang/reflect/package.html @@ -0,0 +1,18 @@ +<html> +<body> +<p>Contains interfaces that extend <code>Signature</code> to provide +additional information about each possible join point signature. This +additional information can be accessed by casting a Signature object +to the appropriate type, i.e.</p> +<pre> + before(): call(* *(..)) { + MethodSignature sig = (MethodSignature)thisJoinPoint.getSignature(); + ... + } +</pre> + +<p>This package also contains <code>SourceLocation</code> that provides +information about the location in source code that corresponds to a +particular join point.</p> +</body> +</html> diff --git a/runtime/src/org/aspectj/runtime/CFlow.java b/runtime/src/org/aspectj/runtime/CFlow.java new file mode 100644 index 000000000..59165f8a7 --- /dev/null +++ b/runtime/src/org/aspectj/runtime/CFlow.java @@ -0,0 +1,39 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.runtime; + +public class CFlow { + private Object _aspect; + + public CFlow() { + this(null); + } + + public CFlow(Object _aspect) { + this._aspect = _aspect; + } + + public Object getAspect() { + return this._aspect; + } + + public void setAspect(Object _aspect) { + this._aspect = _aspect; + } + + public Object get(int index) { + return null; + } +} diff --git a/runtime/src/org/aspectj/runtime/internal/AroundClosure.java b/runtime/src/org/aspectj/runtime/internal/AroundClosure.java new file mode 100644 index 000000000..1d5ad810a --- /dev/null +++ b/runtime/src/org/aspectj/runtime/internal/AroundClosure.java @@ -0,0 +1,39 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.runtime.internal; + +public abstract class AroundClosure { + //private Object[] state; + + public AroundClosure(/* Object[] state */) { + // this.state = state; + } + + protected Object[] state; + protected Object[] preInitializationState; + public AroundClosure(Object[] state) { + this.state = state; + } + + public Object[] getPreInitializationState() { + return preInitializationState; + } + + /** + * This takes in the same arguments as are passed to the proceed + * call in the around advice (with primitives coerced to Object types) + */ + public abstract Object run(Object[] args) throws Throwable; +} diff --git a/runtime/src/org/aspectj/runtime/internal/CFlowPlusState.java b/runtime/src/org/aspectj/runtime/internal/CFlowPlusState.java new file mode 100644 index 000000000..b47addde3 --- /dev/null +++ b/runtime/src/org/aspectj/runtime/internal/CFlowPlusState.java @@ -0,0 +1,32 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.runtime.internal; + +public class CFlowPlusState extends org.aspectj.runtime.CFlow { + private Object[] state; + + public CFlowPlusState(Object[] state) { + this.state = state; + } + + public CFlowPlusState(Object[] state, Object _aspect) { + super(_aspect); + this.state = state; + } + + public Object get(int index) { + return state[index]; + } +} diff --git a/runtime/src/org/aspectj/runtime/internal/CFlowStack.java b/runtime/src/org/aspectj/runtime/internal/CFlowStack.java new file mode 100644 index 000000000..d3b18c710 --- /dev/null +++ b/runtime/src/org/aspectj/runtime/internal/CFlowStack.java @@ -0,0 +1,107 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.runtime.internal; + +import org.aspectj.lang.NoAspectBoundException; +import org.aspectj.runtime.CFlow; + +import java.util.Stack; +import java.util.Hashtable; +import java.util.Vector; +import java.util.Enumeration; + +public class CFlowStack { + private Hashtable stacks = new Hashtable(); + private Thread cached_thread; + private Stack cached_stack; + private int change_count = 0; + private static final int COLLECT_AT = 20000; + private static final int MIN_COLLECT_AT = 100; + + private synchronized Stack getThreadStack() { + if (Thread.currentThread() != cached_thread) { + cached_thread = Thread.currentThread(); + cached_stack = (Stack)stacks.get(cached_thread); + if (cached_stack == null) { + cached_stack = new Stack(); + stacks.put(cached_thread, cached_stack); + } + change_count++; + // Collect more often if there are many threads, but not *too* often + if (change_count > Math.max(MIN_COLLECT_AT, COLLECT_AT/stacks.size())) { + Stack dead_stacks = new Stack(); + for (Enumeration e = stacks.keys(); e.hasMoreElements(); ) { + Thread t = (Thread)e.nextElement(); + if (!t.isAlive()) dead_stacks.push(t); + } + for (Enumeration e = dead_stacks.elements(); e.hasMoreElements(); ) { + Thread t = (Thread)e.nextElement(); + stacks.remove(t); + } + change_count = 0; + } + } + return cached_stack; + } + + //XXX dangerous, try to remove + public void push(Object obj) { + getThreadStack().push(obj); + } + + public void pushInstance(Object obj) { + getThreadStack().push(new CFlow(obj)); + } + + public void push(Object[] obj) { + getThreadStack().push(new CFlowPlusState(obj)); + } + + public void pop() { + getThreadStack().pop(); + } + + public Object peek() { + Stack stack = getThreadStack(); + if (stack.isEmpty()) throw new org.aspectj.lang.NoAspectBoundException(); + return (Object)stack.peek(); + } + + public Object get(int index) { + return peekCFlow().get(index); + } + + public Object peekInstance() { + CFlow cf = peekCFlow(); + if (cf != null ) return cf.getAspect(); + else throw new NoAspectBoundException(); + } + + public CFlow peekCFlow() { + Stack stack = getThreadStack(); + if (stack.isEmpty()) return null; + return (CFlow)stack.peek(); + } + + public CFlow peekTopCFlow() { + Stack stack = getThreadStack(); + if (stack.isEmpty()) return null; + return (CFlow)stack.get(0); + } + + public boolean isValid() { + return !getThreadStack().isEmpty(); + } +} diff --git a/runtime/src/org/aspectj/runtime/internal/Conversions.java b/runtime/src/org/aspectj/runtime/internal/Conversions.java new file mode 100644 index 000000000..24be02d0d --- /dev/null +++ b/runtime/src/org/aspectj/runtime/internal/Conversions.java @@ -0,0 +1,145 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.runtime.internal; + +public final class Conversions { + // Can't make instances of me + private Conversions() {} + + // we might want to keep a cache of small integers around + public static Object intObject(int i) { + return new Integer(i); + } + public static Object shortObject(short i) { + return new Short(i); + } + public static Object byteObject(byte i) { + return new Byte(i); + } + public static Object charObject(char i) { + return new Character(i); + } + public static Object longObject(long i) { + return new Long(i); + } + public static Object floatObject(float i) { + return new Float(i); + } + public static Object doubleObject(double i) { + return new Double(i); + } + public static Object booleanObject(boolean i) { + return new Boolean(i); + } + public static Object voidObject() { + return null; + } + + + public static int intValue(Object o) { + if (o == null) { + return 0; + } else if (o instanceof Number) { + return ((Number)o).intValue(); + } else { + throw new ClassCastException(o.getClass().getName() + + " can not be converted to int"); + } + } + public static long longValue(Object o) { + if (o == null) { + return 0; + } else if (o instanceof Number) { + return ((Number)o).longValue(); + } else { + throw new ClassCastException(o.getClass().getName() + + " can not be converted to long"); + } + } + public static float floatValue(Object o) { + if (o == null) { + return 0; + } else if (o instanceof Number) { + return ((Number)o).floatValue(); + } else { + throw new ClassCastException(o.getClass().getName() + + " can not be converted to float"); + } + } + public static double doubleValue(Object o) { + if (o == null) { + return 0; + } else if (o instanceof Number) { + return ((Number)o).doubleValue(); + } else { + throw new ClassCastException(o.getClass().getName() + + " can not be converted to double"); + } + } + public static byte byteValue(Object o) { + if (o == null) { + return 0; + } else if (o instanceof Number) { + return ((Number)o).byteValue(); + } else { + throw new ClassCastException(o.getClass().getName() + + " can not be converted to byte"); + } + } + public static short shortValue(Object o) { + if (o == null) { + return 0; + } else if (o instanceof Number) { + return ((Number)o).shortValue(); + } else { + throw new ClassCastException(o.getClass().getName() + + " can not be converted to short"); + } + } + public static char charValue(Object o) { + if (o == null) { + return 0; + } else if (o instanceof Character) { + return ((Character)o).charValue(); + } else { + throw new ClassCastException(o.getClass().getName() + + " can not be converted to char"); + } + } + public static boolean booleanValue(Object o) { + if (o == null) { + return false; + } else if (o instanceof Boolean) { + return ((Boolean)o).booleanValue(); + } else { + throw new ClassCastException(o.getClass().getName() + + " can not be converted to boolean"); + } + } + + /** + * identity function for now. This is not typed to "void" because we happen + * to know that in Java, any void context (i.e., {@link ExprStmt}) + * can also handle a return value. + */ + public static Object voidValue(Object o) { + if (o == null) { + return o; + } else { + // !!! this may be an error in the future + return o; + } + } +} diff --git a/runtime/src/org/aspectj/runtime/internal/PerObjectMap.java b/runtime/src/org/aspectj/runtime/internal/PerObjectMap.java new file mode 100644 index 000000000..5a4b5b765 --- /dev/null +++ b/runtime/src/org/aspectj/runtime/internal/PerObjectMap.java @@ -0,0 +1,35 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.runtime.internal; + +import org.aspectj.runtime.CFlow; + + +// REQUIRES JAVA 2!!!!!!!!!!!!!!!! +import java.util.WeakHashMap; + +public class PerObjectMap { + private WeakHashMap map = new WeakHashMap(); + + public boolean hasAspect(Object o) { return map.containsKey(o); } + + public Object aspectOf(Object o) { + return map.get(o); + } + + public void bind(Object object, Object _aspect) { + map.put(object, _aspect); + } +} diff --git a/runtime/src/org/aspectj/runtime/reflect/AdviceSignatureImpl.java b/runtime/src/org/aspectj/runtime/reflect/AdviceSignatureImpl.java new file mode 100644 index 000000000..7ea1ceff1 --- /dev/null +++ b/runtime/src/org/aspectj/runtime/reflect/AdviceSignatureImpl.java @@ -0,0 +1,58 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.runtime.reflect; + +import org.aspectj.lang.*; +import org.aspectj.lang.reflect.*; + +import java.lang.reflect.Modifier; + +class AdviceSignatureImpl extends CodeSignatureImpl implements AdviceSignature { + Class returnType; + + AdviceSignatureImpl(int modifiers, String name, Class declaringType, + Class[] parameterTypes, String[] parameterNames, Class[] exceptionTypes, + Class returnType) + { + super(modifiers, name, declaringType, parameterTypes, parameterNames, + exceptionTypes); + this.returnType = returnType; + } + + AdviceSignatureImpl(String stringRep) { + super(stringRep); + } + /* name is consistent with reflection API + before and after always return Void.TYPE + (some around also return Void.Type) */ + public Class getReturnType() { + if (returnType == null) returnType = extractType(6); + return returnType; + } + + String toString(StringMaker sm) { + //XXX this signature needs a lot of work + StringBuffer buf = new StringBuffer("ADVICE: "); + buf.append(sm.makeModifiersString(getModifiers())); + if (sm.includeArgs) buf.append(sm.makeTypeName(getReturnType())); + if (sm.includeArgs) buf.append(" "); + buf.append(sm.makePrimaryTypeName(getDeclaringType())); + buf.append("."); + buf.append(getName()); + sm.addSignature(buf, getParameterTypes()); + sm.addThrows(buf, getExceptionTypes()); + return buf.toString(); + } +} diff --git a/runtime/src/org/aspectj/runtime/reflect/CatchClauseSignatureImpl.java b/runtime/src/org/aspectj/runtime/reflect/CatchClauseSignatureImpl.java new file mode 100644 index 000000000..638b7fea9 --- /dev/null +++ b/runtime/src/org/aspectj/runtime/reflect/CatchClauseSignatureImpl.java @@ -0,0 +1,48 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.runtime.reflect; + +import org.aspectj.lang.*; +import org.aspectj.lang.reflect.*; + +class CatchClauseSignatureImpl extends SignatureImpl implements CatchClauseSignature { + Class parameterType; + String parameterName; + + CatchClauseSignatureImpl(Class declaringType, + Class parameterType, String parameterName) + { + super(0, "catch", declaringType); + this.parameterType = parameterType; + this.parameterName = parameterName; + } + + CatchClauseSignatureImpl(String stringRep) { + super(stringRep); + } + + public Class getParameterType() { + if (parameterType == null) parameterType = extractType(3); + return parameterType; + } + public String getParameterName() { + if (parameterName == null) parameterName = extractString(4); + return parameterName; + } + + String toString(StringMaker sm) { + return "catch(" + sm.makeTypeName(getParameterType()) + ")"; + } +} diff --git a/runtime/src/org/aspectj/runtime/reflect/CodeSignatureImpl.java b/runtime/src/org/aspectj/runtime/reflect/CodeSignatureImpl.java new file mode 100644 index 000000000..1045152d4 --- /dev/null +++ b/runtime/src/org/aspectj/runtime/reflect/CodeSignatureImpl.java @@ -0,0 +1,49 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.runtime.reflect; + +import org.aspectj.lang.*; +import org.aspectj.lang.reflect.*; + +abstract class CodeSignatureImpl extends MemberSignatureImpl implements CodeSignature { + Class[] parameterTypes; + String[] parameterNames; + Class[] exceptionTypes; + + CodeSignatureImpl(int modifiers, String name, Class declaringType, + Class[] parameterTypes, String[] parameterNames, Class[] exceptionTypes) + { + super(modifiers, name, declaringType); + this.parameterTypes = parameterTypes; + this.parameterNames = parameterNames; + this.exceptionTypes = exceptionTypes; + } + CodeSignatureImpl(String stringRep) { + super(stringRep); + } + + public Class[] getParameterTypes() { + if (parameterTypes == null) parameterTypes = extractTypes(3); + return parameterTypes; + } + public String[] getParameterNames() { + if (parameterNames == null) parameterNames = extractStrings(4); + return parameterNames; + } + public Class[] getExceptionTypes() { + if (exceptionTypes == null) exceptionTypes = extractTypes(5); + return exceptionTypes; + } +} diff --git a/runtime/src/org/aspectj/runtime/reflect/ConstructorSignatureImpl.java b/runtime/src/org/aspectj/runtime/reflect/ConstructorSignatureImpl.java new file mode 100644 index 000000000..49b0827f9 --- /dev/null +++ b/runtime/src/org/aspectj/runtime/reflect/ConstructorSignatureImpl.java @@ -0,0 +1,41 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.runtime.reflect; + +import org.aspectj.lang.reflect.*; +import org.aspectj.lang.*; + +class ConstructorSignatureImpl extends CodeSignatureImpl implements ConstructorSignature { + ConstructorSignatureImpl(int modifiers, Class declaringType, + Class[] parameterTypes, String[] parameterNames, Class[] exceptionTypes) + { + super(modifiers, "<init>", declaringType, parameterTypes, parameterNames, exceptionTypes); + } + + ConstructorSignatureImpl(String stringRep) { + super(stringRep); + } + + public String getName() { return "<init>"; } + + String toString(StringMaker sm) { + StringBuffer buf = new StringBuffer(); + buf.append(sm.makeModifiersString(getModifiers())); + buf.append(sm.makePrimaryTypeName(getDeclaringType())); + sm.addSignature(buf, getParameterTypes()); + sm.addThrows(buf, getExceptionTypes()); + return buf.toString(); + } +} diff --git a/runtime/src/org/aspectj/runtime/reflect/Factory.java b/runtime/src/org/aspectj/runtime/reflect/Factory.java new file mode 100644 index 000000000..781286389 --- /dev/null +++ b/runtime/src/org/aspectj/runtime/reflect/Factory.java @@ -0,0 +1,90 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.runtime.reflect; + +import org.aspectj.lang.*; +import org.aspectj.lang.reflect.*; + +public final class Factory { + Class lexicalClass; + ClassLoader lookupClassLoader; + String filename; + public Factory(String filename, Class lexicalClass) { + //System.out.println("making + this.filename = filename; + this.lexicalClass = lexicalClass; + lookupClassLoader = lexicalClass.getClassLoader(); + } + + public JoinPoint.StaticPart makeSJP(String kind, Signature sig, SourceLocation loc) { + return new JoinPointImpl.StaticPartImpl(kind, sig, loc); + } + + public JoinPoint.StaticPart makeSJP(String kind, Signature sig, int l, int c) { + return new JoinPointImpl.StaticPartImpl(kind, sig, makeSourceLoc(l, c)); + } + + public JoinPoint.StaticPart makeSJP(String kind, Signature sig, int l) { + return new JoinPointImpl.StaticPartImpl(kind, sig, makeSourceLoc(l, -1)); + } + + public static JoinPoint makeJP(JoinPoint.StaticPart staticPart, + Object _this, Object target, Object[] args) + { + return new JoinPointImpl(staticPart, _this, target, args); + } + + public MethodSignature makeMethodSig(String stringRep) { + MethodSignatureImpl ret = new MethodSignatureImpl(stringRep); + ret.setLookupClassLoader(lookupClassLoader); + return ret; + } + + public ConstructorSignature makeConstructorSig(String stringRep) { + ConstructorSignatureImpl ret = new ConstructorSignatureImpl(stringRep); + ret.setLookupClassLoader(lookupClassLoader); + return ret; + } + + public FieldSignature makeFieldSig(String stringRep) { + FieldSignatureImpl ret = new FieldSignatureImpl(stringRep); + ret.setLookupClassLoader(lookupClassLoader); + return ret; + } + + public AdviceSignature makeAdviceSig(String stringRep) { + AdviceSignatureImpl ret = new AdviceSignatureImpl(stringRep); + ret.setLookupClassLoader(lookupClassLoader); + return ret; + } + + public InitializerSignature makeInitializerSig(String stringRep) { + InitializerSignatureImpl ret = new InitializerSignatureImpl(stringRep); + ret.setLookupClassLoader(lookupClassLoader); + return ret; + } + + public CatchClauseSignature makeCatchClauseSig(String stringRep) { + CatchClauseSignatureImpl ret = new CatchClauseSignatureImpl(stringRep); + ret.setLookupClassLoader(lookupClassLoader); + return ret; + } + + + public SourceLocation makeSourceLoc(int line, int col) + { + return new SourceLocationImpl(lexicalClass, this.filename, line, col); + } +} diff --git a/runtime/src/org/aspectj/runtime/reflect/FieldSignatureImpl.java b/runtime/src/org/aspectj/runtime/reflect/FieldSignatureImpl.java new file mode 100644 index 000000000..5ab058edd --- /dev/null +++ b/runtime/src/org/aspectj/runtime/reflect/FieldSignatureImpl.java @@ -0,0 +1,51 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.runtime.reflect; + +import org.aspectj.lang.reflect.*; +import org.aspectj.lang.*; + +import java.lang.reflect.Modifier; + +public class FieldSignatureImpl extends MemberSignatureImpl implements FieldSignature { + Class fieldType; + + FieldSignatureImpl(int modifiers, String name, Class declaringType, + Class fieldType) + { + super(modifiers, name, declaringType); + this.fieldType = fieldType; + } + + FieldSignatureImpl(String stringRep) { + super(stringRep); + } + + public Class getFieldType() { + if (fieldType == null) fieldType = extractType(3); + return fieldType; + } + + String toString(StringMaker sm) { + StringBuffer buf = new StringBuffer(); + buf.append(sm.makeModifiersString(getModifiers())); + if (sm.includeArgs) buf.append(sm.makeTypeName(getFieldType())); + if (sm.includeArgs) buf.append(" "); + buf.append(sm.makePrimaryTypeName(getDeclaringType())); + buf.append("."); + buf.append(getName()); + return buf.toString(); + } +} diff --git a/runtime/src/org/aspectj/runtime/reflect/InitializerSignatureImpl.java b/runtime/src/org/aspectj/runtime/reflect/InitializerSignatureImpl.java new file mode 100644 index 000000000..f7065d673 --- /dev/null +++ b/runtime/src/org/aspectj/runtime/reflect/InitializerSignatureImpl.java @@ -0,0 +1,44 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.runtime.reflect; + +import org.aspectj.lang.reflect.*; +import org.aspectj.lang.*; + +import java.lang.reflect.Modifier; + +class InitializerSignatureImpl extends CodeSignatureImpl implements InitializerSignature { + InitializerSignatureImpl(int modifiers, Class declaringType) { + super(modifiers, Modifier.isStatic(modifiers) ? "<clinit>" : "<init>", declaringType, EMPTY_CLASS_ARRAY, + EMPTY_STRING_ARRAY, EMPTY_CLASS_ARRAY); + } + + InitializerSignatureImpl(String stringRep) { + super(stringRep); + } + + public String getName() { + return Modifier.isStatic(getModifiers()) ? "<clinit>": "<init>"; + } + + String toString(StringMaker sm) { + StringBuffer buf = new StringBuffer(); + buf.append(sm.makeModifiersString(getModifiers())); + buf.append(sm.makePrimaryTypeName(getDeclaringType())); + buf.append("."); + buf.append(getName()); + return buf.toString(); + } +} diff --git a/runtime/src/org/aspectj/runtime/reflect/JoinPointImpl.java b/runtime/src/org/aspectj/runtime/reflect/JoinPointImpl.java new file mode 100644 index 000000000..573d8b338 --- /dev/null +++ b/runtime/src/org/aspectj/runtime/reflect/JoinPointImpl.java @@ -0,0 +1,76 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.runtime.reflect; + +import org.aspectj.lang.*; + +import org.aspectj.lang.reflect.SourceLocation; + +class JoinPointImpl implements JoinPoint { + static class StaticPartImpl implements JoinPoint.StaticPart { + String kind; + Signature signature; + SourceLocation sourceLocation; + + public StaticPartImpl(String kind, Signature signature, SourceLocation sourceLocation) { + this.kind = kind; + this.signature = signature; + this.sourceLocation = sourceLocation; + } + + public String getKind() { return kind; } + public Signature getSignature() { return signature; } + public SourceLocation getSourceLocation() { return sourceLocation; } + + String toString(StringMaker sm) { + StringBuffer buf = new StringBuffer(); + buf.append(sm.makeKindName(getKind())); + buf.append("("); + buf.append(((SignatureImpl)getSignature()).toString(sm)); + buf.append(")"); + return buf.toString(); + } + + public final String toString() { return toString(StringMaker.middleStringMaker); } + public final String toShortString() { return toString(StringMaker.shortStringMaker); } + public final String toLongString() { return toString(StringMaker.longStringMaker); } + } + + Object _this; + Object target; + Object[] args; + org.aspectj.lang.JoinPoint.StaticPart staticPart; + + public JoinPointImpl(org.aspectj.lang.JoinPoint.StaticPart staticPart, Object _this, Object target, Object[] args) { + this.staticPart = staticPart; + this._this = _this; + this.target = target; + this.args = args; + } + + public Object getThis() { return _this; } + public Object getTarget() { return target; } + public Object[] getArgs() { return args; } + + public org.aspectj.lang.JoinPoint.StaticPart getStaticPart() { return staticPart; } + + public String getKind() { return staticPart.getKind(); } + public Signature getSignature() { return staticPart.getSignature(); } + public SourceLocation getSourceLocation() { return staticPart.getSourceLocation(); } + + public final String toString() { return staticPart.toString(); } + public final String toShortString() { return staticPart.toShortString(); } + public final String toLongString() { return staticPart.toLongString(); } +} diff --git a/runtime/src/org/aspectj/runtime/reflect/MemberSignatureImpl.java b/runtime/src/org/aspectj/runtime/reflect/MemberSignatureImpl.java new file mode 100644 index 000000000..3097a1abf --- /dev/null +++ b/runtime/src/org/aspectj/runtime/reflect/MemberSignatureImpl.java @@ -0,0 +1,28 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.runtime.reflect; + +import org.aspectj.lang.*; +import org.aspectj.lang.reflect.*; + +abstract class MemberSignatureImpl extends SignatureImpl implements MemberSignature { + MemberSignatureImpl(int modifiers, String name, Class declaringType) { + super(modifiers, name, declaringType); + } + + public MemberSignatureImpl(String stringRep) { + super(stringRep); + } +} diff --git a/runtime/src/org/aspectj/runtime/reflect/MethodSignatureImpl.java b/runtime/src/org/aspectj/runtime/reflect/MethodSignatureImpl.java new file mode 100644 index 000000000..4318dc9ca --- /dev/null +++ b/runtime/src/org/aspectj/runtime/reflect/MethodSignatureImpl.java @@ -0,0 +1,55 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.runtime.reflect; + +import org.aspectj.lang.*; +import org.aspectj.lang.reflect.*; +import java.lang.reflect.Modifier; + +class MethodSignatureImpl extends CodeSignatureImpl implements MethodSignature { + Class returnType; + + MethodSignatureImpl(int modifiers, String name, Class declaringType, + Class[] parameterTypes, String[] parameterNames, Class[] exceptionTypes, + Class returnType) + { + super(modifiers, name, declaringType, parameterTypes, parameterNames, + exceptionTypes); + this.returnType = returnType; + } + + MethodSignatureImpl(String stringRep) { + super(stringRep); + } + + /* name is consistent with reflection API */ + public Class getReturnType() { + if (returnType == null) returnType = extractType(6); + return returnType; + } + + String toString(StringMaker sm) { + StringBuffer buf = new StringBuffer(); + buf.append(sm.makeModifiersString(getModifiers())); + if (sm.includeArgs) buf.append(sm.makeTypeName(getReturnType())); + if (sm.includeArgs) buf.append(" "); + buf.append(sm.makePrimaryTypeName(getDeclaringType())); + buf.append("."); + buf.append(getName()); + sm.addSignature(buf, getParameterTypes()); + sm.addThrows(buf, getExceptionTypes()); + return buf.toString(); + } +} diff --git a/runtime/src/org/aspectj/runtime/reflect/SignatureImpl.java b/runtime/src/org/aspectj/runtime/reflect/SignatureImpl.java new file mode 100644 index 000000000..ec46bbbab --- /dev/null +++ b/runtime/src/org/aspectj/runtime/reflect/SignatureImpl.java @@ -0,0 +1,188 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.runtime.reflect; + +import org.aspectj.lang.*; +import org.aspectj.lang.reflect.*; + +import java.util.*; + +abstract class SignatureImpl implements Signature { + int modifiers = -1; + String name; + Class declaringType; + + SignatureImpl(int modifiers, String name, Class declaringType) { + this.modifiers = modifiers; + this.name = name; + this.declaringType = declaringType; + } + + abstract String toString(StringMaker sm); + + public final String toString() { return toString(StringMaker.middleStringMaker); } + public final String toShortString() { return toString(StringMaker.shortStringMaker); } + public final String toLongString() { return toString(StringMaker.longStringMaker); } + + public int getModifiers() { + if (modifiers == -1) modifiers = extractInt(0); + return modifiers; + } + public String getName() { + if (name == null) name = extractString(1); + return name; + } + public Class getDeclaringType() { + if (declaringType == null) declaringType = extractType(2); + return declaringType; + } + + + String fullTypeName(Class type) { + if (type == null) return "ANONYMOUS"; + if (type.isArray()) return fullTypeName(type.getComponentType()) + "[]"; + return type.getName().replace('$', '.'); + } + + String stripPackageName(String name) { + int dot = name.lastIndexOf('.'); + if (dot == -1) return name; + return name.substring(dot+1); + } + + String shortTypeName(Class type) { + if (type == null) return "ANONYMOUS"; + if (type.isArray()) return shortTypeName(type.getComponentType()) + "[]"; + return stripPackageName(type.getName()).replace('$', '.'); + } + + void addFullTypeNames(StringBuffer buf, Class[] types) { + for (int i = 0; i < types.length; i++) { + if (i > 0) buf.append(", "); + buf.append(fullTypeName(types[i])); + } + } + void addShortTypeNames(StringBuffer buf, Class[] types) { + for (int i = 0; i < types.length; i++) { + if (i > 0) buf.append(", "); + buf.append(shortTypeName(types[i])); + } + } + + void addTypeArray(StringBuffer buf, Class[] types) { + addFullTypeNames(buf, types); + } + + // lazy version + String stringRep; + ClassLoader lookupClassLoader = null; + + public void setLookupClassLoader(ClassLoader loader) { + this.lookupClassLoader = loader; + } + + private ClassLoader getLookupClassLoader() { + if (lookupClassLoader == null) lookupClassLoader = this.getClass().getClassLoader(); + return lookupClassLoader; + } + + public SignatureImpl(String stringRep) { + this.stringRep = stringRep; + } + + static final char SEP = '-'; + + String extractString(int n) { + //System.out.println(n + ": from " + stringRep); + + int startIndex = 0; + int endIndex = stringRep.indexOf(SEP); + while (n-- > 0) { + startIndex = endIndex+1; + endIndex = stringRep.indexOf(SEP, startIndex); + } + if (endIndex == -1) endIndex = stringRep.length(); + + //System.out.println(" " + stringRep.substring(startIndex, endIndex)); + + return stringRep.substring(startIndex, endIndex); + } + + int extractInt(int n) { + String s = extractString(n); + return Integer.parseInt(s, 16); + } + + Class extractType(int n) { + String s = extractString(n); + return makeClass(s); + } + + static Hashtable prims = new Hashtable(); + static { + prims.put("void", Void.TYPE); + prims.put("boolean", Boolean.TYPE); + prims.put("byte", Byte.TYPE); + prims.put("char", Character.TYPE); + prims.put("short", Short.TYPE); + prims.put("int", Integer.TYPE); + prims.put("long", Long.TYPE); + prims.put("float", Float.TYPE); + prims.put("double", Double.TYPE); + } + + Class makeClass(String s) { + if (s.equals("*")) return null; + Class ret = (Class)prims.get(s); + if (ret != null) return ret; + try { + /* The documentation of Class.forName explains why this is the right thing + * better than I could here. + */ + ClassLoader loader = getLookupClassLoader(); + if (loader == null) { + return Class.forName(s); + } else { + return loader.loadClass(s); + } + } catch (ClassNotFoundException e) { + //System.out.println("null for: " + s); + //XXX there should be a better return value for this + return ClassNotFoundException.class; + } + } + + static String[] EMPTY_STRING_ARRAY = new String[0]; + static Class[] EMPTY_CLASS_ARRAY = new Class[0]; + + static final String INNER_SEP = ":"; + + String[] extractStrings(int n) { + String s = extractString(n); + StringTokenizer st = new StringTokenizer(s, INNER_SEP); + final int N = st.countTokens(); + String[] ret = new String[N]; + for (int i = 0; i < N; i++) ret[i]= st.nextToken(); + return ret; + } + Class[] extractTypes(int n) { + String s = extractString(n); + StringTokenizer st = new StringTokenizer(s, INNER_SEP); + final int N = st.countTokens(); + Class[] ret = new Class[N]; + for (int i = 0; i < N; i++) ret[i]= makeClass(st.nextToken()); + return ret; + } +} diff --git a/runtime/src/org/aspectj/runtime/reflect/SourceLocationImpl.java b/runtime/src/org/aspectj/runtime/reflect/SourceLocationImpl.java new file mode 100644 index 000000000..6a04283b3 --- /dev/null +++ b/runtime/src/org/aspectj/runtime/reflect/SourceLocationImpl.java @@ -0,0 +1,43 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.runtime.reflect; + +import org.aspectj.lang.*; +import org.aspectj.lang.reflect.*; + +class SourceLocationImpl implements SourceLocation { + Class withinType; + String fileName; + int line; + int column; + + SourceLocationImpl(Class withinType, String fileName, int line, int column) { + this.withinType = withinType; + this.fileName = fileName; + this.line = line; + this.column = column; + } + + public Class getWithinType() { return withinType; } + public String getFileName() { return fileName; } + public int getLine() { return line; } + public int getColumn() { return column; } + + public String toString() { + return getFileName() + ":" + getLine() + + ((getColumn() == -1) ? "" : ":" + getColumn()); + } +} + diff --git a/runtime/src/org/aspectj/runtime/reflect/StringMaker.java b/runtime/src/org/aspectj/runtime/reflect/StringMaker.java new file mode 100644 index 000000000..bc46f992c --- /dev/null +++ b/runtime/src/org/aspectj/runtime/reflect/StringMaker.java @@ -0,0 +1,134 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.runtime.reflect; + +import org.aspectj.lang.*; +import org.aspectj.lang.reflect.*; + +import java.lang.reflect.Modifier; + +class StringMaker { + boolean shortTypeNames = true; + boolean includeArgs = true; + boolean includeThrows = false; + boolean includeModifiers = false; + boolean shortPrimaryTypeNames = false; + + boolean includeJoinPointTypeName = true; + boolean includeEnclosingPoint = true; + boolean shortKindName = true; + + static StringMaker shortStringMaker; + static { + shortStringMaker = new StringMaker(); + shortStringMaker.shortTypeNames = true; + shortStringMaker.includeArgs = false; + shortStringMaker.includeThrows = false; + shortStringMaker.includeModifiers = false; + shortStringMaker.shortPrimaryTypeNames = true; + + shortStringMaker.includeJoinPointTypeName = false; + shortStringMaker.includeEnclosingPoint = false; + + } + + static StringMaker middleStringMaker; + static { + middleStringMaker = new StringMaker(); + middleStringMaker.shortTypeNames = true; + middleStringMaker.includeArgs = true; + middleStringMaker.includeThrows = false; + middleStringMaker.includeModifiers = false; + middleStringMaker.shortPrimaryTypeNames = false; + } + + static StringMaker longStringMaker; + static { + longStringMaker = new StringMaker(); + longStringMaker.shortTypeNames = false; + longStringMaker.includeArgs = true; + longStringMaker.includeThrows = false; + longStringMaker.includeModifiers = true; + longStringMaker.shortPrimaryTypeNames = false; + longStringMaker.shortKindName = false; + } + + String makeKindName(String name) { + int dash = name.lastIndexOf('-'); + if (dash == -1) return name; + return name.substring(dash+1); + } + + String makeModifiersString(int modifiers) { + if (!includeModifiers) return ""; + String str = Modifier.toString(modifiers); + if (str.length() == 0) return ""; + return str + " "; + } + + String stripPackageName(String name) { + int dot = name.lastIndexOf('.'); + if (dot == -1) return name; + return name.substring(dot+1); + } + + String makeTypeName(Class type, boolean shortName) { + if (type == null) return "ANONYMOUS"; + if (type.isArray()) return makeTypeName(type.getComponentType(), shortName) + "[]"; + if (shortName) { + return stripPackageName(type.getName()).replace('$', '.'); + } else { + return type.getName().replace('$', '.'); + } + } + + public String makeTypeName(Class type) { + return makeTypeName(type, shortTypeNames); + } + + public String makePrimaryTypeName(Class type) { + return makeTypeName(type, shortPrimaryTypeNames); + } + + public void addTypeNames(StringBuffer buf, Class[] types) { + for (int i = 0; i < types.length; i++) { + if (i > 0) buf.append(", "); + buf.append(makeTypeName(types[i])); + } + } + + public void addSignature(StringBuffer buf, Class[] types) { + if (types == null) return; + if (!includeArgs) { + if (types.length == 0) { + buf.append("()"); + return; + } else { + buf.append("(..)"); + return; + } + } + buf.append("("); + addTypeNames(buf, types); + buf.append(")"); + } + + public void addThrows(StringBuffer buf, Class[] types) { + if (!includeThrows || types == null || types.length == 0) return; + + buf.append(" throws "); + addTypeNames(buf, types); + } +} diff --git a/runtime/testsrc/RuntimeModuleTests.java b/runtime/testsrc/RuntimeModuleTests.java new file mode 100644 index 000000000..ced7ba2ca --- /dev/null +++ b/runtime/testsrc/RuntimeModuleTests.java @@ -0,0 +1,30 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + + +// default package + +import junit.framework.*; + +public class RuntimeModuleTests extends TestCase { + + public static Test suite() { + TestSuite suite = new TestSuite(RuntimeModuleTests.class.getName()); + suite.addTestSuite(RuntimeModuleTests.class); // minimum 1 test (testNothing) + return suite; + } + + public RuntimeModuleTests(String name) { super(name); } + + public void testNothing() {} +} diff --git a/util/src/.cvsignore b/util/src/.cvsignore new file mode 100644 index 000000000..a3f0b1b77 --- /dev/null +++ b/util/src/.cvsignore @@ -0,0 +1 @@ +*.lst diff --git a/util/src/org/aspectj/util/CollectionUtil.java b/util/src/org/aspectj/util/CollectionUtil.java new file mode 100644 index 000000000..637362c1c --- /dev/null +++ b/util/src/org/aspectj/util/CollectionUtil.java @@ -0,0 +1,60 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.util; + + +import java.util.*; + +public class CollectionUtil { + public static final String[] NO_STRINGS = new String[0]; + + + public static List getListInMap(Map map, Object key) { + List list = (List)map.get(key); + if (list == null) { + list = new ArrayList(); + map.put(key, list); + } + return list; + } + + public static SortedSet getSortedSetInMap(Map map, Object key) { + SortedSet list = (SortedSet)map.get(key); + if (list == null) { + list = new TreeSet(); + map.put(key, list); + } + return list; + } + + public static Set getSetInMap(Map map, Object key) { + Set list = (Set)map.get(key); + if (list == null) { + list = new HashSet(); + map.put(key, list); + } + return list; + } + + public static Map getMapInMap(Map map, Object key) { + Map list = (Map)map.get(key); + if (list == null) { + list = new HashMap(); + map.put(key, list); + } + return list; + } + +} diff --git a/util/src/org/aspectj/util/ConfigParser.java b/util/src/org/aspectj/util/ConfigParser.java new file mode 100644 index 000000000..dab133510 --- /dev/null +++ b/util/src/org/aspectj/util/ConfigParser.java @@ -0,0 +1,290 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + + + +package org.aspectj.util; + +import java.util.*; +import java.io.*; + +public class ConfigParser { + Location location; + protected List files = new LinkedList(); + private boolean fileParsed = false; + + public List getFiles() { return files; } + + public void parseCommandLine(String[] argsArray) throws ParseException { + location = new CommandLineLocation(); + LinkedList args = new LinkedList(); + for (int i = 0; i < argsArray.length; i++) { + args.add(new Arg(argsArray[i], location)); + } + parseArgs(args); + } + + public void parseConfigFile(File configFile) throws ParseException { + if (fileParsed == true) { + throw new ParseException("The file has already been parsed.", null); + } else { + parseConfigFileHelper(configFile); + } + } + + /** + * @throws ParseException if the config file has already been prased. + */ + private void parseConfigFileHelper(File configFile) { + if (!configFile.exists()) { + showError("file does not exist: " + configFile.getPath()); + return; + } + + LinkedList args = new LinkedList(); + int lineNum = 0; + + try { + BufferedReader stream = + new BufferedReader(new FileReader(configFile)); + String line = null; + while ( (line = stream.readLine()) != null) { + lineNum += 1; + line = stripWhitespaceAndComments(line); + if (line.length() == 0) continue; + args.add(new Arg(line, new SourceLocation(configFile, lineNum))); + } + } catch (IOException e) { + location = new SourceLocation(configFile, lineNum); + showError("error reading config file: " + e.toString()); + } + parseArgs(args); + fileParsed = true; + } + + File getCurrentDir() { + return location.getDirectory(); + } + + String stripSingleLineComment(String s, String commentString) { + int commentStart = s.indexOf(commentString); + if (commentStart == -1) return s; + else return s.substring(0, commentStart); + } + + String stripWhitespaceAndComments(String s) { + s = stripSingleLineComment(s, "//"); + s = stripSingleLineComment(s, "#"); + s = s.trim(); + if (s.startsWith("\"") && s.endsWith("\"")) { + s = s.substring(1, s.length()-1); + } + return s; + } + + + /** ??? We would like to call a showNonFatalError method here + * to show all errors in config files before aborting the compilation + */ + protected void addFile(File sourceFile) { + if (!sourceFile.isFile()) { + showError("source file does not exist: " + sourceFile.getPath()); + } + + files.add(sourceFile); + } + + void addFileOrPattern(File sourceFile) { + if (sourceFile.getName().equals("*.java")) { + addFiles(sourceFile.getParentFile(), new FileFilter() { + public boolean accept(File f) { + return f != null && f.getName().endsWith(".java"); + }}); + } else if (sourceFile.getName().equals("*.aj")) { + addFiles(sourceFile.getParentFile(), new FileFilter() { + public boolean accept(File f) { + return f != null && f.getName().endsWith(".aj"); + }}); + } else { + addFile(sourceFile); + } + } + + void addFiles(File dir, FileFilter filter) { + if (dir == null) dir = new File(System.getProperty("user.dir")); + + if (!dir.isDirectory()) { + showError("can't find " + dir.getPath()); + } + + File[] files = dir.listFiles(filter); + if (files.length == 0) { + showWarning("no matching files found in: " + dir); + } + + for (int i = 0; i < files.length; i++) { + addFile(files[i]); + } + } + + protected void parseOption(String arg, LinkedList args) { + showWarning("unrecognized option: " + arg); + } + + protected void showWarning(String message) { + if (location != null) { + message += " at " + location.toString(); + } + System.err.println(message); + } + + protected void showError(String message) { + throw new ParseException(message, location); + } + + void parseArgs(LinkedList args) { + while (args.size() > 0) parseOneArg(args); + } + + protected Arg removeArg(LinkedList args) { + if (args.size() == 0) { + showError("value missing"); + return null; + } else { + return (Arg)args.removeFirst(); + } + } + + protected String removeStringArg(LinkedList args) { + Arg arg = removeArg(args); + if (arg == null) return null; + return arg.getValue(); + } + + boolean isSourceFileName(String s) { + if (s.endsWith(".java")) return true; + if (s.endsWith(".aj")) return true; + if (s.endsWith(".ajava")) { + showWarning(".ajava is deprecated, replace with .aj or .java: " + s); + return true; + } + return false; + } + + void parseOneArg(LinkedList args) { + Arg arg = removeArg(args); + String v = arg.getValue(); + location = arg.getLocation(); + if (v.startsWith("@")) { + parseImportedConfigFile(v.substring(1)); + } else if (v.equals("-argfile")) { + parseConfigFileHelper(makeFile(removeArg(args).getValue())); + } else if (isSourceFileName(v)) { + addFileOrPattern(makeFile(v)); + } else { + parseOption(arg.getValue(), args); + } + } + + protected void parseImportedConfigFile(String relativeFilePath) { + parseConfigFileHelper(makeFile(relativeFilePath)); + } + + public File makeFile(String name) { + return makeFile(getCurrentDir(), name); + } + + File makeFile(File dir, String name) { + name = name.replace('/', File.separatorChar); + File ret = new File(name); + if (dir == null || ret.isAbsolute()) return ret; + return new File(dir, name); + } + + + protected static class Arg { + private Location location; + private String value; + public Arg(String value, Location location) { + this.value = value; + this.location = location; + } + + public void setValue(String value) { + this.value = value; + } + + public void setLocation(Location location) { + this.location = location; + } + + public String getValue() { return value; } + public Location getLocation() { return location; } + } + + static abstract class Location { + public abstract File getFile(); + public abstract File getDirectory(); + public abstract int getLine(); + public abstract String toString(); + } + + static class SourceLocation extends Location { + private int line; + private File file; + public SourceLocation(File file, int line) { + this.line = line; + this.file = file; + } + + public File getFile() { return file; } + public File getDirectory() { return file.getParentFile(); } + public int getLine() { return line; } + + public String toString() { + return file.getPath()+":"+line; + } + } + + static class CommandLineLocation extends Location { + public File getFile() { + return new File(System.getProperty("user.dir")); + } + + public File getDirectory() { + return new File(System.getProperty("user.dir")); + } + public int getLine() { return -1; } + public String toString() { + return "command-line"; + } + } + + public static class ParseException extends RuntimeException { + private Location location; + + public ParseException(String message, Location location) { + super(message); + this.location = location; + } + + public int getLine() { + if (location == null) return -1; + return location.getLine(); + } + public File getFile() { + if (location == null) return null; + return location.getFile(); + } + } +} diff --git a/util/src/org/aspectj/util/FileUtil.java b/util/src/org/aspectj/util/FileUtil.java new file mode 100644 index 000000000..a0d445cfe --- /dev/null +++ b/util/src/org/aspectj/util/FileUtil.java @@ -0,0 +1,1048 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + +package org.aspectj.util; + +import java.io.*; +import java.net.*; +import java.util.*; +import java.util.zip.*; +import java.util.zip.ZipFile; + + +/** + * + */ +public class FileUtil { + /** unmodifiable List of String source file suffixes (including leading ".") */ + public static final List SOURCE_SUFFIXES + = Collections.unmodifiableList(Arrays.asList(new String[] { ".java", ".aj"})); + + final static int[] INT_RA = new int[0]; + + /** accept all files */ + public static final FileFilter ALL = new FileFilter() { + public boolean accept(File f) { return true; } + }; + + /** @return true if file path has a zip/jar suffix */ + public static boolean hasZipSuffix(File file) { + return ((null != file) && hasZipSuffix(file.getPath())); + } + + /** @return true if path ends with .zip or .jar */ + public static boolean hasZipSuffix(String path) { + return ((null != path) && (0 != zipSuffixLength(path))); + } + + /** @return 0 if file has no zip/jar suffix or 4 otherwise */ + public static int zipSuffixLength(File file) { + return (null == file ? 0 : zipSuffixLength(file.getPath())); + } + + /** @return 0 if no zip/jar suffix or 4 otherwise */ + public static int zipSuffixLength(String path) { + return (null == path ? 0 + : path.endsWith(".zip") ? 4 + : path.endsWith(".jar") ? 4 : 0); + } + + /** @return true if file path has a source suffix */ + public static boolean hasSourceSuffix(File file) { + return ((null != file) && hasSourceSuffix(file.getPath())); + } + + /** @return true if path ends with .java or .aj */ + public static boolean hasSourceSuffix(String path) { + return ((null != path) && (0 != sourceSuffixLength(path))); + } + + /** @return 0 if file has no source suffix or the length of the suffix otherwise */ + public static int sourceSuffixLength(File file) { + return (null == file ? 0 : sourceSuffixLength(file.getPath())); + } + + /** @return 0 if no source suffix or the length of the suffix otherwise */ + public static int sourceSuffixLength(String path) { + if (LangUtil.isEmpty(path)) { + return 0; + } + + for (Iterator iter = SOURCE_SUFFIXES.iterator(); iter.hasNext();) { + String suffix = (String) iter.next(); + if (path.endsWith(suffix) + || path.toLowerCase().endsWith(suffix)) { + return suffix.length(); + } + } + return 0; + } + + /** @return true if this is a readable directory */ + public static boolean canReadDir(File dir) { + return ((null != dir) && dir.canRead() && dir.isDirectory()); + } + + /** @return true if this is a readable file */ + public static boolean canReadFile(File file) { + return ((null != file) && file.canRead() && file.isFile()); + } + + /** @return true if dir is a writable directory */ + public static boolean canWriteDir(File dir) { + return ((null != dir) && dir.canWrite() && dir.isDirectory()); + } + + /** @return true if this is a writable file */ + public static boolean canWriteFile(File file) { + return ((null != file) && file.canWrite() && file.isFile()); + } + + /** @throws IllegalArgumentException unless file is readable and not a directory */ + public static void throwIaxUnlessCanReadFile(File file, String label) { + if (!canReadFile(file)) { + throw new IllegalArgumentException(label + " not readable file: " + file); + } + } + + /** @throws IllegalArgumentException unless dir is a readable directory */ + public static void throwIaxUnlessCanReadDir(File dir, String label) { + if (!canReadDir(dir)) { + throw new IllegalArgumentException(label + " not readable dir: " + dir); + } + } + + /** @throws IllegalArgumentException unless file is readable and not a directory */ + public static void throwIaxUnlessCanWriteFile(File file, String label) { + if (!canWriteFile(file)) { + throw new IllegalArgumentException(label + " not writable file: " + file); + } + } + + /** @throws IllegalArgumentException unless dir is a readable directory */ + public static void throwIaxUnlessCanWriteDir(File dir, String label) { + if (!canWriteDir(dir)) { + throw new IllegalArgumentException(label + " not writable dir: " + dir); + } + } + + /** @return array same length as input, with String paths */ + public static String[] getPaths(File[] files) { + if ((null == files) || (0 == files.length)) { + return new String[0]; + } + String[] result = new String[files.length]; + for (int i = 0; i < result.length; i++) { + if (null != files[i]) { + result[i] = files[i].getPath(); + } + } + return result; + } + + /** @return array same length as input, with String paths */ + public static String[] getPaths(List files) { + final int size = (null == files ? 0 : files.size()); + if (0 == size) { + return new String[0]; + } + String[] result = new String[size]; + for (int i = 0; i < size; i++) { + File file = (File) files.get(i); + if (null != file) { + result[i] = file.getPath(); + } + } + return result; + } + + + /** + * Extract the name of a class from the path to its file. + * If the basedir is null, then the class is assumed to be in + * the default package unless the classFile has one of the + * top-level suffixes { com, org, java, javax } as a parent directory. + * @param basedir the File of the base directory (prefix of classFile) + * @param classFile the File of the class to extract the name for + * @throws IllegalArgumentException if classFile is null or does not end with + * ".class" or a non-null basedir is not a prefix of classFile + */ + public static String fileToClassName(File basedir, File classFile) { + LangUtil.throwIaxIfNull(classFile, "classFile"); + String classFilePath = normalizedPath(classFile); + if (!classFilePath.endsWith(".class")) { + String m = classFile + " does not end with .class"; + throw new IllegalArgumentException(m); + } + classFilePath = classFilePath.substring(0, classFilePath.length()-6); + if (null != basedir) { + String basePath = normalizedPath(basedir); + if (!classFilePath.startsWith(basePath)) { + String m = classFile + " does not start with " + basedir; + throw new IllegalArgumentException(m); + } + classFilePath = classFilePath.substring(basePath.length()+1); + } else { + final String[] suffixes = new String[] { "com", "org", "java", "javax"}; + boolean found = false; + for (int i = 0; !found && (i < suffixes.length); i++) { + int loc = classFilePath.indexOf(suffixes[i] + "/"); + if ((0 == loc) + || ((-1 != loc) && ('/' == classFilePath.charAt(loc-1)))) { + classFilePath = classFilePath.substring(loc); + found = true; + } + } + if (!found) { + int loc = classFilePath.lastIndexOf("/"); + if (-1 != loc) { // treat as default package + classFilePath = classFilePath.substring(loc+1); + } + } + } + return classFilePath.replace('/', '.'); + } + + /** + * Normalize path for comparisons by rendering absolute, + * clipping basedir prefix, + * trimming and changing '\\' to '/' + * @param file the File with the path to normalize + * @param basedir the File for the prefix of the file to normalize - ignored if null + * @return "" if null or normalized path otherwise + * @throws IllegalArgumentException if basedir is not a prefix of file + */ + public static String normalizedPath(File file, File basedir) { + String filePath = normalizedPath(file); + if (null != basedir) { + String basePath = normalizedPath(basedir); + if (filePath.startsWith(basePath)) { + filePath = filePath.substring(basePath.length()); + if (filePath.startsWith("/")) { + filePath = filePath.substring(1); + } + } + } + return filePath; + } + + /** + * Flatten File[] to String. + * @param files the File[] of paths to flatten - null ignored + * @param infix the String infix to use - null treated as File.pathSeparator + */ + public static String flatten(File[] files, String infix) { + if (LangUtil.isEmpty(files)) { + return ""; + } + return flatten(getPaths(files), infix); + } + + /** + * Flatten File[] to String. + * @param files the File[] of paths to flatten - null ignored + * @param infix the String infix to use - null treated as File.pathSeparator + */ + public static String flatten(String[] paths, String infix) { + if (null == infix) { + infix = File.pathSeparator; + } + StringBuffer result = new StringBuffer(); + boolean first = true; + for (int i = 0; i < paths.length; i++) { + String path = paths[i]; + if (null == path) { + continue; + } + if (first) { + first = false; + } else { + result.append(infix); + } + result.append(path); + } + return result.toString(); + } + + /** + * Normalize path for comparisons by rendering absolute + * trimming and changing '\\' to '/' + * @return "" if null or normalized path otherwise + */ + public static String normalizedPath(File file) { + return (null == file ? "" : weakNormalize(file.getAbsolutePath())); + } + + /** + * Weakly normalize path for comparisons by + * trimming and changing '\\' to '/' + */ + public static String weakNormalize(String path) { + if (null != path) { + path = path.replace('\\', '/').trim(); + } + return path; + } + + /** @return array same length as input, with String absolute paths */ + public static String[] getAbsolutePaths(File[] files) { + if ((null == files) || (0 == files.length)) { + return new String[0]; + } + String[] result = new String[files.length]; + for (int i = 0; i < result.length; i++) { + if (null != files[i]) { + result[i] = files[i].getAbsolutePath(); + } + } + return result; + } + + /** + * Recursively delete the contents of dir, but not the dir itself + * @return the total number of files deleted + */ + public static int deleteContents(File dir) { + return deleteContents(dir, ALL); + } + + /** + * Recursively delete some contents of dir, but not the dir itself. + * This deletes any subdirectory which is empty after its files + * are deleted. + * @return the total number of files deleted + */ + public static int deleteContents(File dir, FileFilter filter) { + return deleteContents(dir, filter, true); + } + + /** + * Recursively delete some contents of dir, but not the dir itself. + * If deleteEmptyDirs is true, this deletes any subdirectory + * which is empty after its files are deleted. + * @param dir the File directory (if a file, the the file is deleted) + * @return the total number of files deleted + */ + public static int deleteContents(File dir, FileFilter filter, + boolean deleteEmptyDirs) { + if (null == dir) { + throw new IllegalArgumentException("null dir"); + } + if ((!dir.exists()) || (!dir.canWrite())) { + return 0; + } + if (!dir.isDirectory()) { + dir.delete(); + return 1; + } + String[] fromFiles = dir.list(); + int result = 0; + for (int i = 0; i < fromFiles.length; i++) { + String string = fromFiles[i]; + File file = new File(dir, string); + if ((null == filter) || filter.accept(file)) { + if (file.isDirectory()) { + result += deleteContents(file, filter, deleteEmptyDirs); + if (deleteEmptyDirs && (0 == file.list().length)) { + file.delete(); + } + } else { + file.delete(); + result++; + } + } + } + return result; + } + + /** + * Copy contents of fromDir into toDir + * @param fromDir must exist and be readable + * @param toDir must exist or be creatable and be writable + * @return the total number of files copied + */ + public static int copyDir(File fromDir, File toDir) throws IOException { + return copyDir(fromDir, toDir, null, null); + } + + /** + * Recursively copy files in fromDir (with any fromSuffix) to toDir, + * replacing fromSuffix with toSuffix if any. + * This silently ignores dirs and files that are not readable + * but throw IOException for directories that are not writable. + * This does not clean out the original contents of toDir. + * (subdirectories are not renamed per directory rules) + * @param fromSuffix select files with this suffix - select all if null or empty + * @param toSuffix replace fromSuffix with toSuffix in the destination file + * name - ignored if null or empty, + * appended to name if fromSuffix is null or empty + * @return the total number of files copied + */ + public static int copyDir(File fromDir, File toDir, + final String fromSuffix, String toSuffix) throws IOException { + return copyDir(fromDir, toDir, fromSuffix, toSuffix, (FileFilter) null); + } + + /** map name to result, removing any fromSuffix and adding any toSuffix */ + private static String map(String name, String fromSuffix, String toSuffix) { + if (null != name) { + if (null != fromSuffix) { + name = name.substring(0, name.length()-fromSuffix.length()); + } + if (null != toSuffix) { + name = name + toSuffix; + } + } + return name; + } + + /** + * Recursively copy files in fromDir (with any fromSuffix) to toDir, + * replacing fromSuffix with toSuffix if any, + * and adding the destination file to any collector. + * This silently ignores dirs and files that are not readable + * but throw IOException for directories that are not writable. + * This does not clean out the original contents of toDir. + * (subdirectories are not renamed per directory rules) + * This calls any delegate FilenameFilter to collect any selected file. + * @param fromSuffix select files with this suffix - select all if null or empty + * @param toSuffix replace fromSuffix with toSuffix in the destination file + * name - ignored if null or empty, + * appended to name if fromSuffix is null or empty + * @param collector the List sink for destination files - ignored if null + * @return the total number of files copied + */ + public static int copyDir(File fromDir, File toDir, final String fromSuffix, + final String toSuffix, final List collector) throws IOException { + //int before = collector.size(); + if (null == collector) { + return copyDir(fromDir, toDir, fromSuffix, toSuffix); + } else { + FileFilter collect = new FileFilter() { + public boolean accept(File pathname) { + return collector.add(pathname); + } + }; + return copyDir(fromDir, toDir, fromSuffix, toSuffix, collect); + } + } + + /** + * Recursively copy files in fromDir (with any fromSuffix) to toDir, + * replacing fromSuffix with toSuffix if any. + * This silently ignores dirs and files that are not readable + * but throw IOException for directories that are not writable. + * This does not clean out the original contents of toDir. + * (subdirectories are not renamed per directory rules) + * This calls any delegate FilenameFilter to collect any selected file. + * @param fromSuffix select files with this suffix - select all if null or empty + * @param toSuffix replace fromSuffix with toSuffix in the destination file + * name - ignored if null or empty, + * appended to name if fromSuffix is null or empty + * @return the total number of files copied + */ + public static int copyDir(File fromDir, File toDir, final String fromSuffix, + final String toSuffix, final FileFilter delegate) throws IOException { + + if ((null == fromDir) || (!fromDir.canRead())) { + return 0; + } + final boolean haveSuffix = ((null != fromSuffix) && (0 < fromSuffix.length())); + final int slen = (!haveSuffix ? 0 : fromSuffix.length()); + + if (!toDir.exists()) { + toDir.mkdirs(); + } + final String[] fromFiles; + if (!haveSuffix) { + fromFiles = fromDir.list(); + } else { + FilenameFilter filter = new FilenameFilter() { + public boolean accept(File dir, String name) { + return (new File(dir, name).isDirectory() + || (name.endsWith(fromSuffix))); + } + }; + fromFiles = fromDir.list(filter); + } + int result = 0; + final int MAX = (null == fromFiles ? 0 : fromFiles.length); + for (int i = 0; i < MAX; i++) { + String filename = fromFiles[i]; + File fromFile = new File(fromDir, filename); + if (fromFile.canRead()) { + if (fromFile.isDirectory()) { + result += copyDir(fromFile, new File(toDir, filename), fromSuffix, toSuffix, delegate); + } else if (fromFile.isFile()) { + if (haveSuffix) { + filename = filename.substring(0, filename.length()-slen); + } + if (null != toSuffix) { + filename = filename + toSuffix; + } + File targetFile = new File(toDir, filename); + if ((null == delegate) || delegate.accept(targetFile)) { + copyFile(fromFile, targetFile); + } + result++; + } + } + } + return result; + } + + /** + * Recursively list files in srcDir. + * @return ArrayList with String paths of File under srcDir (relative to srcDir) + */ + public static String[] listFiles(File srcDir) { + ArrayList result = new ArrayList(); + if ((null != srcDir) && srcDir.canRead()) { + listFiles(srcDir, null, result); + } + return (String[]) result.toArray(new String[0]); + } + + private static void listFiles(final File baseDir, String dir, ArrayList result) { + final String dirPrefix = (null == dir ? "" : dir + "/"); + final File dirFile = (null == dir ? baseDir : new File(baseDir.getPath() + "/" + dir)); + final String[] files = dirFile.list(); + for (int i = 0; i < files.length; i++) { + File f = new File(dirFile, files[i]); + String path = dirPrefix + files[i]; + if (f.isDirectory()) { + listFiles(baseDir, path, result); + } else { + result.add(path); + } + } + } + + public static final FileFilter aspectjSourceFileFilter = new FileFilter() { + public boolean accept(File pathname) { + String name = pathname.getName().toLowerCase(); + return name.endsWith(".java") || name.endsWith(".aj"); + } + }; + + + /** + * Recursively list files in srcDir. + * @return ArrayList with String paths of File under srcDir (relative to srcDir) + */ + public static File[] listFiles(File srcDir, FileFilter fileFilter) { + ArrayList result = new ArrayList(); + if ((null != srcDir) && srcDir.canRead()) { + listFiles(srcDir, result, fileFilter); + } + return (File[]) result.toArray(new File[result.size()]); + } + + private static void listFiles(final File baseDir, ArrayList result, FileFilter filter) { + File[] files = baseDir.listFiles(); + for (int i = 0; i < files.length; i++) { + File f = files[i]; + if (f.isDirectory()) { + listFiles(f, result, filter); + } else { + if (filter.accept(f)) result.add(f); + } + } + } + + /** + * Convert String[] paths to File[] as offset of base directory + * @param basedir the non-null File base directory for File to create with paths + * @param paths the String[] of paths to create + * @return File[] with same length as paths + */ + public static File[] getBaseDirFiles( + File basedir, + String[] paths) { + return getBaseDirFiles(basedir, paths, (String[]) null); + } + + /** + * Convert String[] paths to File[] as offset of base directory + * @param basedir the non-null File base directory for File to create with paths + * @param paths the String[] of paths to create + * @param suffixes the String[] of suffixes to limit sources to - ignored if null + * @return File[] with same length as paths + */ + public static File[] getBaseDirFiles( + File basedir, + String[] paths, + String[] suffixes) { + LangUtil.throwIaxIfNull(basedir, "basedir"); + LangUtil.throwIaxIfNull(paths, "paths"); + File[] result = null; + if (!LangUtil.isEmpty(suffixes)) { + ArrayList list = new ArrayList(); + for (int i = 0; i < paths.length; i++) { + boolean listed = false; + String path = paths[i]; + for (int j = 0; !listed && (j < suffixes.length); j++) { + String suffix = suffixes[j]; + if (listed = path.endsWith(suffix)) { + list.add(new File(basedir, paths[i])); + } + } + } + result = (File[]) list.toArray(new File[0]); + } else { + result = new File[paths.length]; + for (int i = 0; i < result.length; i++) { + result[i] = new File(basedir, paths[i]); + } + } + return result; + } + + /** + * Copy files from source dir into destination directory, + * creating any needed directories. This differs from copyDir in not + * being recursive; each input with the source dir creates a full path. + * @param srcDir an existing, readable directory containing relativePaths files + * @param relativePaths a set of paths relative to srcDir to readable File to copy + * @param destDir an existing, writable directory to copy files to + * @throws IllegalArgumentException if input invalid, IOException if operations fail + */ + public static File[] copyFiles(File srcDir, String[] relativePaths, File destDir) + throws IllegalArgumentException, IOException { + final String[] paths = relativePaths; + throwIaxUnlessCanReadDir(srcDir, "srcDir"); + throwIaxUnlessCanWriteDir(destDir, "destDir"); + LangUtil.throwIaxIfNull(paths, "relativePaths"); + File[] result = new File[paths.length]; + for (int i = 0; i < paths.length; i++) { + String path = paths[i]; + LangUtil.throwIaxIfNull(path, "relativePaths-entry"); + File src = new File(srcDir, relativePaths[i]); + throwIaxUnlessCanReadFile(src, "src-entry"); + File dest = new File(destDir, path); + File destParent = dest.getParentFile(); + if (!destParent.exists()) { + destParent.mkdirs(); + } + LangUtil.throwIaxIfFalse(canWriteDir(destParent), "dest-entry-parent"); + copyFile(src, dest); + result[i] = dest; + } + return result; + } + + /** copy fromFile to toFile */ + public static void copyFile(File fromFile, File toFile) throws IOException { + FileInputStream in = null; + FileOutputStream out = null; + try { + in = new FileInputStream(fromFile); + out = new FileOutputStream(toFile); + copyStream(in, out); + } finally { + if (out != null) { + out.close(); + } + if (in != null) { + in.close(); + } + } + } + + /** do line-based copying */ + public static void copyStream(DataInputStream in, PrintStream out) throws IOException { + LangUtil.throwIaxIfNull(in, "in"); + LangUtil.throwIaxIfNull(in, "out"); + String s; + while (null != (s = in.readLine())) { + out.println(s); + } + } + + public static void copyStream(InputStream in, OutputStream out) throws IOException { + final int MAX = 4096; + byte[] buf = new byte[MAX]; + for (int bytesRead = in.read(buf, 0, MAX); + bytesRead != -1; + bytesRead = in.read(buf, 0, MAX)) { + out.write(buf, 0, bytesRead); + } + } + + public static void copyStream(Reader in, Writer out) throws IOException { + final int MAX = 4096; + char[] buf = new char[MAX]; + for (int bytesRead = in.read(buf, 0, MAX); + bytesRead != -1; + bytesRead = in.read(buf, 0, MAX)) { + out.write(buf, 0, bytesRead); + } + } + + private static boolean isValidFileName(String input) { + return ((null != input) && (-1 == input.indexOf(File.pathSeparator))); + } + + /** + * Make a new child directory of parent + * @param parent a File for the parent (writable) + * @param child a prefix for the child directory + * @return a File dir that exists with parentDir as the parent file or null + */ + public static File makeNewChildDir(File parent, String child) { + if (null == parent || ! parent.canWrite() || !parent.isDirectory()) { + throw new IllegalArgumentException("bad parent: " + parent); + } else if (null == child) { + child = "makeNewChildDir"; + } else if (!isValidFileName(child)) { + throw new IllegalArgumentException("bad child: " + child); + } + File result = new File(parent, child); + int safety = 1000; + for (String suffix = FileUtil.randomFileString(); + ((0 < --safety) && result.exists()); + suffix = FileUtil.randomFileString()) { + result = new File(parent, child+suffix); + } + if ((null == result) || result.exists()) { + System.err.println("exhausted files for child dir in " + parent); + return null; + } + return ((result.mkdirs() && result.exists()) ? result : null); + } + + /** + * Make a new temporary directory in the same directory + * that the system uses for temporary files, or if + * that files, in the current directory. + * @param name the preferred (simple) name of the directory - may be null. + * @return File of an existing new temp dir, or null if unable to create + */ + public static File getTempDir(String name) { + if (null == name) { + name = "FileUtil_getTempDir"; + } else if (!isValidFileName(name)) { + throw new IllegalArgumentException(" invalid: " + name); + } + File result = null; + File tempFile = null; + try { + tempFile = File.createTempFile("ignoreMe", ".txt"); + File tempParent = tempFile.getParentFile(); + result = makeNewChildDir(tempParent, name); + } catch (IOException t) { + result = makeNewChildDir(new File("."), name); + } finally { + if (null != tempFile) { + tempFile.delete(); + } + } + return result; + } + + public static URL[] getFileURLs(File[] files) { // XXX prints errors to System.err + if ((null == files) || (0 == files.length)) { + return new URL[0]; + } + URL[] result = new URL[files.length]; // XXX dangerous non-copy... + for (int i = 0; i < result.length; i++) { + result[i] = getFileURL(files[i]); + } + return result; + } + + /** + * Get URL for a File. + * This appends "/" for directories. + * prints errors to System.err + * @param file the File to convert to URL (not null) + */ + public static URL getFileURL(File file) { + LangUtil.throwIaxIfNull(file, "file"); + URL result = null; + try { + String url = "file:" + file.getAbsolutePath().replace('\\', '/'); + result = new URL(url + (file.isDirectory() ? "/" : "")); + } catch (MalformedURLException e) { + String m = "Util.makeURL(\"" + file.getPath() + "\" MUE " + e.getMessage(); + System.err.println(m); + } + return result; + } + + + + /** + * Write contents to file, returning null on success or error message otherwise. + * This tries to make any necessary parent directories first. + */ + public static String writeAsString(File file, String contents) { + LangUtil.throwIaxIfNull(file, "file"); + if (null == contents) { + contents = ""; + } + Writer out = null; + try { + File parentDir = file.getParentFile(); + if (!parentDir.exists() && !parentDir.mkdirs()) { + return "unable to make parent dir for " + file; + } + Reader in = new StringReader(contents); + out = new FileWriter(file); + FileUtil.copyStream(in, out); + return null; + } catch (IOException e) { + return LangUtil.unqualifiedClassName(e) + " writing " + file + + ": " + e.getMessage(); + } finally { + if (null != out) { + try { out.close(); } + catch (IOException e) {} // ignored + } + } + } + + + /** + * Reads an int array with our encoding + */ + public static int[] readIntArray(DataInputStream s) throws IOException { + int len = s.readInt(); + int[] ret = new int[len]; + for (int i=0; i < len; i++) ret[i] = s.readInt(); + return ret; + } + + + /** + * Reads an int array with our encoding + */ + public static void writeIntArray(DataOutputStream s, int[] a) throws IOException { + int len = a.length; + s.writeInt(len); + for (int i=0; i < len; i++) s.writeInt(a[i]); + } + + + + + /** + * Returns the contents of this file as a String + */ + public static String readAsString(File file) throws IOException { + BufferedReader r = new BufferedReader(new FileReader(file)); + StringBuffer b = new StringBuffer(); + while (true) { + int ch = r.read(); + if (ch == -1) break; + b.append((char)ch); + } + r.close(); + return b.toString(); + } + + /** + * Returns the contents of this stream as a String + */ + public static String readAsString(InputStream in) throws IOException { + BufferedReader r = new BufferedReader(new InputStreamReader(in)); + StringBuffer b = new StringBuffer(); + while (true) { + int ch = r.read(); + if (ch == -1) break; + b.append((char)ch); + } + in.close(); + r.close(); + return b.toString(); + } + + + /** + * Returns the contents of this file as a byte[] + */ + public static byte[] readAsByteArray(File file) throws IOException { + FileInputStream in = new FileInputStream(file); + byte[] ret = FileUtil.readAsByteArray(in); + in.close(); + return ret; + } + + + /** + * Reads this input stream and returns contents as a byte[] + */ + public static byte[] readAsByteArray(InputStream inStream) throws IOException { + int size = 1024; + byte[] ba = new byte[size]; + int readSoFar = 0; + + while (true) { + int nRead = inStream.read(ba, readSoFar, size-readSoFar); + if (nRead == -1) break; + readSoFar += nRead; + if (readSoFar == size) { + int newSize = size * 2; + byte[] newBa = new byte[newSize]; + System.arraycopy(ba, 0, newBa, 0, size); + ba = newBa; + size = newSize; + } + } + + byte[] newBa = new byte[readSoFar]; + System.arraycopy(ba, 0, newBa, 0, readSoFar); + return newBa; + } + final static String FILECHARS = "abcdefghijklmnopqrstuvxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + /** @return String usable in a File name which is random */ + + /** @return semi-random String of length 6 usable as filename suffix */ + static String randomFileString() { + final double FILECHARS_length = FILECHARS.length(); + final int LEN = 6; + final char[] result = new char[LEN]; + int index = (int) (Math.random() * 6d); + for (int i = 0; i < LEN; i++) { + if (index >= LEN) { + index = 0; + } + result[index++] = FILECHARS.charAt((int) (Math.random() * FILECHARS_length)); + } + return new String(result); + } + + public static InputStream getStreamFromZip(String zipFile, String name) { + try { + ZipFile zf = new ZipFile(zipFile); + try { + ZipEntry entry = zf.getEntry(name); + return zf.getInputStream(entry); + } finally { + //??? is it safe not to close this zf.close(); + } + } catch (IOException ioe) { + return null; + } + } + + + public static void extractJar(String zipFile, String outDir) throws IOException { + ZipInputStream zs = new ZipInputStream(new FileInputStream(zipFile)); + ZipEntry entry; + while ( (entry = zs.getNextEntry()) != null) { + if (entry.isDirectory()) continue; + byte[] in = readAsByteArray(zs); + + File outFile = new File(outDir + "/" + entry.getName()); + //if (!outFile.getParentFile().exists()) + //System.err.println("parent: " + outFile.getParentFile()); + //System.err.println("parent: " + outFile.getParentFile()); + outFile.getParentFile().mkdirs(); + FileOutputStream os = new FileOutputStream(outFile); + os.write(in); + os.close(); + zs.closeEntry(); + } + zs.close(); + } + + private FileUtil() { throw new Error("utility class"); } + + + + /** + * Do line-based search for literal text in source files, + * returning file:line where found. + * @param sought the String text to seek in the file + * @param sources the List of String paths to the source files + * @param listAll if false, only list first match in file + * @param errorSink the PrintStream to print any errors to (one per line) + * (use null to silently ignore errors) + * @return List of String of the form file:line for each found entry + * (never null, might be empty) + */ + public static List lineSeek(String sought, List sources, boolean listAll, + PrintStream errorSink) { + if (LangUtil.isEmpty(sought) || LangUtil.isEmpty(sources)) { + return Collections.EMPTY_LIST; + } + ArrayList result = new ArrayList(); + for (Iterator iter = sources.iterator(); iter.hasNext();) { + String path = (String) iter.next(); + String error = lineSeek(sought, path, listAll, result); + if ((null != error) && (null != errorSink)) { + errorSink.println(error); + } + } + return result; + } + + /** + * Do line-based search for literal text in source file, + * returning line where found as a String + * in the form {sourcePath}:line:column submitted to the + * collecting parameter sink. + * Any error is rendered to String and returned as the result. + * + * @param sought the String text to seek in the file + * @param sources the List of String paths to the source files + * @param listAll if false, only list first match in file + * @param List sink the List for String entries of the form {sourcePath}:line:column + * @return String error if any, or add String entries to sink + */ + public static String lineSeek(String sought, String sourcePath, boolean listAll, + ArrayList sink) { + if (LangUtil.isEmpty(sought) || LangUtil.isEmpty(sourcePath)) { + return "nothing sought"; + } + if (LangUtil.isEmpty(sourcePath)) { + return "no sourcePath"; + } + final File file = new File(sourcePath); + if (!file.canRead() || !file.isFile()) { + return "sourcePath not a readable file"; + } + int lineNum = 0; + FileReader fin = null; + try { + fin = new FileReader(file); + BufferedReader reader = new BufferedReader(fin); + String line; + while (null != (line = reader.readLine())) { + lineNum++; + int loc = line.indexOf(sought); + if (-1 != loc) { + sink.add(sourcePath + ":" + lineNum + ":" + loc); + if (!listAll) { + break; + } + } + } + } catch (IOException e) { + return LangUtil.unqualifiedClassName(e) + " reading " + sourcePath + + ":" + lineNum; + } finally { + try { if (null != fin) fin.close(); } + catch (IOException e) {} // ignore + } + return null; + } + + public static BufferedOutputStream makeOutputStream(File file) throws FileNotFoundException { + File parent = file.getParentFile(); + if (parent != null) parent.mkdirs(); + return new BufferedOutputStream(new FileOutputStream(file)); + } + +} diff --git a/util/src/org/aspectj/util/FuzzyBoolean.java b/util/src/org/aspectj/util/FuzzyBoolean.java new file mode 100644 index 000000000..adb66bb41 --- /dev/null +++ b/util/src/org/aspectj/util/FuzzyBoolean.java @@ -0,0 +1,180 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.util; + +import java.util.*; + + +/** This class implements boolean that include a "maybe" + */ + +public abstract class FuzzyBoolean { + public abstract boolean alwaysTrue(); + public abstract boolean alwaysFalse(); + public abstract boolean maybeTrue(); + public abstract boolean maybeFalse(); + + public abstract FuzzyBoolean and(FuzzyBoolean other); + public abstract FuzzyBoolean or(FuzzyBoolean other); + public abstract FuzzyBoolean not(); + + private static class YesFuzzyBoolean extends FuzzyBoolean { + public boolean alwaysFalse() { + return false; + } + + public boolean alwaysTrue() { + return true; + } + + + public boolean maybeFalse() { + return false; + } + + public boolean maybeTrue() { + return true; + } + + public FuzzyBoolean and(FuzzyBoolean other) { + return other; + } + + public FuzzyBoolean not() { + return FuzzyBoolean.NO; + } + + public FuzzyBoolean or(FuzzyBoolean other) { + return this; + } + + public String toString() { + return "YES"; + } + } + private static class NoFuzzyBoolean extends FuzzyBoolean { + public boolean alwaysFalse() { + return true; + } + + public boolean alwaysTrue() { + return false; + } + + + public boolean maybeFalse() { + return true; + } + + public boolean maybeTrue() { + return false; + } + + public FuzzyBoolean and(FuzzyBoolean other) { + return this; + } + + public FuzzyBoolean not() { + return FuzzyBoolean.YES; + } + + public FuzzyBoolean or(FuzzyBoolean other) { + return other; + } + + public String toString() { + return "NO"; + } + } + private static class NeverFuzzyBoolean extends FuzzyBoolean { + public boolean alwaysFalse() { + return true; + } + + public boolean alwaysTrue() { + return false; + } + + + public boolean maybeFalse() { + return true; + } + + public boolean maybeTrue() { + return false; + } + + public FuzzyBoolean and(FuzzyBoolean other) { + return this; + } + + public FuzzyBoolean not() { + return this; + } + + public FuzzyBoolean or(FuzzyBoolean other) { + return this; + } + + public String toString() { + return "NEVER"; + } + } + + private static class MaybeFuzzyBoolean extends FuzzyBoolean { + public boolean alwaysFalse() { + return false; + } + + public boolean alwaysTrue() { + return false; + } + + + public boolean maybeFalse() { + return true; + } + + public boolean maybeTrue() { + return true; + } + + public FuzzyBoolean and(FuzzyBoolean other) { + return other.alwaysFalse() ? other : this; + } + + public FuzzyBoolean not() { + return this; + } + + public FuzzyBoolean or(FuzzyBoolean other) { + return other.alwaysTrue() ? other : this; + } + + public String toString() { + return "MAYBE"; + } + } + + public static final FuzzyBoolean YES = new YesFuzzyBoolean(); + public static final FuzzyBoolean NO = new NoFuzzyBoolean(); + public static final FuzzyBoolean MAYBE = new MaybeFuzzyBoolean(); + public static final FuzzyBoolean NEVER = new NeverFuzzyBoolean(); + + public static final FuzzyBoolean fromBoolean(boolean b) { + return b ? YES : NO; + } + +} diff --git a/util/src/org/aspectj/util/LangUtil.java b/util/src/org/aspectj/util/LangUtil.java new file mode 100644 index 000000000..0a93721f1 --- /dev/null +++ b/util/src/org/aspectj/util/LangUtil.java @@ -0,0 +1,781 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + +package org.aspectj.util; + + +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.lang.reflect.Array; +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.BitSet; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.StringTokenizer; + +/** + * + */ +public class LangUtil { + /** map from String version to String class implemented in that version or later */ + private static final Map VM_CLASSES; + + public static final String EOL; + static { + StringWriter buf = new StringWriter(); + PrintWriter writer = new PrintWriter(buf); + writer.println(""); + String eol = "\n"; + try { + buf.close(); + StringBuffer sb = buf.getBuffer(); + if ((null != sb) || (0 < sb.length())) { + eol = buf.toString(); + } + } catch (Throwable t) { } + EOL = eol; + + HashMap map = new HashMap(); + map.put("1.2", "java.lang.ref.Reference"); + map.put("1.3", "java.lang.reflect.Proxy"); + map.put("1.4", "java.nio.Buffer"); + + VM_CLASSES = Collections.unmodifiableMap(map); + } + + /** + * Detect whether Java version is supported. + * @param version String "1.2" or "1.3" or "1.4" + * @return true if the currently-running VM supports the version + * @throws IllegalArgumentException if version is not known + */ + public static final boolean supportsJava(String version) { + LangUtil.throwIaxIfNull(version, "version"); + String className = (String) VM_CLASSES.get(version); + if (null == className) { + throw new IllegalArgumentException("unknown version: " + version); + } + try { + Class.forName(className); + return true; + } catch (Throwable t) { + return false; + } + } + + /** + * Shorthand for "if null, throw IllegalArgumentException" + * @throws IllegalArgumentException "null {name}" if o is null + */ + public static final void throwIaxIfNull(final Object o, final String name) { + if (null == o) { + String message = "null " + (null == name ? "input" : name); + throw new IllegalArgumentException(message); + } + } + + /** + * Shorthand for "if not null or not assignable, throw IllegalArgumentException" + * @throws IllegalArgumentException "null {name}" if o is null + */ + public static final void throwIaxIfNotAssignable(final Object o, final Class c, final String name) { + throwIaxIfNull(o, name); + if (null != c) { + Class actualClass = o.getClass(); + if (!c.isAssignableFrom(actualClass)) { + String message = name + " not assignable to " + c.getName(); + throw new IllegalArgumentException(message); + } + } + } + + /** + * Shorthand for "if any not null or not assignable, throw IllegalArgumentException" + * @throws IllegalArgumentException "{name} is not assignable to {c}" + */ + public static final void throwIaxIfNotAllAssignable(final Collection collection, + final Class c, final String name) { + throwIaxIfNull(collection, name); + if (null != c) { + for (Iterator iter = collection.iterator(); iter.hasNext();) { + throwIaxIfNotAssignable(iter.next(), c, name); + + } + } + } + /** + * Shorthand for "if false, throw IllegalArgumentException" + * @throws IllegalArgumentException "{message}" if test is false + */ + public static final void throwIaxIfFalse(final boolean test, final String message) { + if (!test) { + throw new IllegalArgumentException(message); + } + } + + /** @return ((null == s) || (0 == s.trim().length())); */ + public static boolean isEmptyTrimmed(String s) { + return ((null == s) || (0 == s.length()) + || (0 == s.trim().length())); + } + + /** @return ((null == s) || (0 == s.length())); */ + public static boolean isEmpty(String s) { + return ((null == s) || (0 == s.length())); + } + + /** @return ((null == ra) || (0 == ra.length)) */ + public static boolean isEmpty(Object[] ra) { + return ((null == ra) || (0 == ra.length)); + } + + /** @return ((null == collection) || (0 == collection.size())) */ + public static boolean isEmpty(Collection collection) { + return ((null == collection) || (0 == collection.size())); + } + + /** + * Splits <code>text</code> at whitespace. + * + * @param text <code>String</code> to split. + */ + public static String[] split(String text) { + return (String[]) strings(text).toArray(new String[0]); + } + + /** + * Splits <code>input</code> at commas, + * trimming any white space. + * + * @param text <code>String</code> to split. + * @return List of String of elements. + */ + public static List commaSplit(String input) { + if (null == input) { + return Collections.EMPTY_LIST; + } + ArrayList result = new ArrayList(); + + if (-1 == input.indexOf(",")) { + result.add(input.trim()); + } else { + StringTokenizer st = new StringTokenizer(input, ","); + while (st.hasMoreTokens()) { + result.add(st.nextToken().trim()); + } + } + return result; + } + + + /** + * Splits strings into a <code>List</code> using a + * <code>StringTokenizer</code>. + * + * @param text <code>String</code> to split. + */ + public static List strings(String text) { + List strings = new ArrayList(); + StringTokenizer tok = new StringTokenizer(text); + while (tok.hasMoreTokens()) { + strings.add(tok.nextToken()); + } + return strings; + } + + /** @return a non-null unmodifiable List */ + public static List safeList(List list) { + return ( + null == list + ? Collections.EMPTY_LIST + : Collections.unmodifiableList(list)); + } + + /** + * Select from input String[] based on suffix-matching + * @param inputs String[] of input - null ignored + * @param suffixes String[] of suffix selectors - null ignored + * @param ignoreCase if true, ignore case + * @return String[] of input that end with any input + */ + public static String[] endsWith(String[] inputs, String[] suffixes, boolean ignoreCase) { + if (LangUtil.isEmpty(inputs) || LangUtil.isEmpty(suffixes)) { + return new String[0]; + } + if (ignoreCase) { + String[] temp = new String[suffixes.length]; + for (int i = 0; i < temp.length; i++) { + String suff = suffixes[i]; + temp[i] = (null == suff ? null : suff.toLowerCase()); + } + suffixes = temp; + } + ArrayList result = new ArrayList(); + for (int i = 0; i < inputs.length; i++) { + String input = inputs[i]; + if (null == input) { + continue; + } + if (!ignoreCase) { + input = input.toLowerCase(); + } + for (int j = 0; j < suffixes.length; j++) { + String suffix = suffixes[j]; + if (null == suffix) { + continue; + } + if (input.endsWith(suffix)) { + result.add(input); + break; + } + } + } + return (String[]) result.toArray(new String[0]); + } + + /** + * copy non-null two-dimensional String[][] + * @see extractOptions(String[], String[][]) + */ + public static String[][] copyStrings(String[][] in) { + String[][] out = new String[in.length][]; + for (int i = 0; i < out.length; i++) { + out[i] = new String[in[i].length]; + System.arraycopy(in[i], 0, out[i], 0, out[i].length); + } + return out; + } + + /** + * Extract options and arguments to input option list, returning remainder. + * The input options will be nullified if not found. e.g., + * <pre>String[] options = new String[][] { new String[] { "-verbose" }, + * new String[] { "-classpath", null } }; + * String[] args = extractOptions(args, options); + * boolean verbose = null != options[0][0]; + * boolean classpath = options[1][1];</pre> + * @param args the String[] input options + * @param options the String[][]options to find in the input args - not null + * for each String[] component the first subcomponent is the option itself, + * and there is one String subcomponent for each additional argument. + * @return String[] of args remaining after extracting options to extracted + */ + public static String[] extractOptions(String[] args, String[][] options) { + if (LangUtil.isEmpty(args) || LangUtil.isEmpty(options) ) { + return args; + } + BitSet foundSet = new BitSet(); + String[] result = new String[args.length]; + int resultIndex = 0; + for (int j = 0; j < args.length; j++) { + boolean found = false; + for (int i = 0; !found && (i < options.length); i++) { + String[] option = options[i]; + LangUtil.throwIaxIfFalse(!LangUtil.isEmpty(option), "options"); + String sought = option[0]; + if (found = sought.equals(args[j])) { + foundSet.set(i); + int doMore = option.length-1; + if (0 < doMore) { + final int MAX = j + doMore; + if (MAX >= args.length) { + String s = "expecting " + doMore + " args after "; + throw new IllegalArgumentException(s + args[j]); + } + for (int k = 1; k < option.length; k++) { + option[k] = args[++j]; + } + } + } + } + if (!found) { + result[resultIndex++] = args[j]; + } + } + + // unset any not found + for (int i = 0; i < options.length; i++) { + if (!foundSet.get(i)) { + options[i][0] = null; + } + } + // fixup remainder + if (resultIndex < args.length) { + String[] temp = new String[resultIndex]; + System.arraycopy(result, 0, temp, 0, resultIndex); + args = temp; + } + + return args; + } + + /** + * Extract options and arguments to input parameter list, returning remainder. + * @param args the String[] input options + * @param validOptions the String[] options to find in the input args - not null + * @param optionArgs the int[] number of arguments for each option in validOptions + * (if null, then no arguments for any option) + * @param extracted the List for the matched options + * @return String[] of args remaining after extracting options to extracted + */ + public static String[] extractOptions(String[] args, String[] validOptions, + int[] optionArgs, List extracted) { + if (LangUtil.isEmpty(args) + || LangUtil.isEmpty(validOptions) ) { + return args; + } + if (null != optionArgs) { + if (optionArgs.length != validOptions.length) { + throw new IllegalArgumentException("args must match options"); + } + } + String[] result = new String[args.length]; + int resultIndex = 0; + for (int j = 0; j < args.length; j++) { + boolean found = false; + for (int i = 0; !found && (i < validOptions.length); i++) { + String sought = validOptions[i]; + int doMore = (null == optionArgs ? 0 : optionArgs[i]); + if (LangUtil.isEmpty(sought)) { + continue; + } + if (found = sought.equals(args[j])) { + if (null != extracted) { + extracted.add(sought); + } + if (0 < doMore) { + final int MAX = j + doMore; + if (MAX >= args.length) { + String s = "expecting " + doMore + " args after "; + throw new IllegalArgumentException(s + args[j]); + } + if (null != extracted) { + while (j < MAX) { + extracted.add(args[++j]); + } + } else { + j = MAX; + } + } + break; + } + } + if (!found) { + result[resultIndex++] = args[j]; + } + } + if (resultIndex < args.length) { + String[] temp = new String[resultIndex]; + System.arraycopy(result, 0, temp, 0, resultIndex); + args = temp; + } + return args; + } + + /** @return String[] of entries in validOptions found in args */ + public static String[] selectOptions(String[] args, String[] validOptions) { + if (LangUtil.isEmpty(args) || LangUtil.isEmpty(validOptions)) { + return new String[0]; + } + ArrayList result = new ArrayList(); + for (int i = 0; i < validOptions.length; i++) { + String sought = validOptions[i]; + if (LangUtil.isEmpty(sought)) { + continue; + } + for (int j = 0; j < args.length; j++) { + if (sought.equals(args[j])) { + result.add(sought); + break; + } + } + } + return (String[]) result.toArray(new String[0]); + } + + /** @return String[] of entries in validOptions found in args */ + public static String[] selectOptions(List args, String[] validOptions) { + if (LangUtil.isEmpty(args) || LangUtil.isEmpty(validOptions)) { + return new String[0]; + } + ArrayList result = new ArrayList(); + for (int i = 0; i < validOptions.length; i++) { + String sought = validOptions[i]; + if (LangUtil.isEmpty(sought)) { + continue; + } + for (Iterator iter = args.iterator(); iter.hasNext();) { + String arg = (String) iter.next(); + if (sought.equals(arg)) { + result.add(sought); + break; + } + } + } + return (String[]) result.toArray(new String[0]); + } + + /** + * Generate variants of String[] options by creating an extra set for + * each option that ends with "-". If none end with "-", then an + * array equal to <code>new String[][] { options }</code> is returned; + * if one ends with "-", then two sets are returned, + * three causes eight sets, etc. + * @return String[][] with each option set. + * @throws IllegalArgumentException if any option is null or empty. + */ + public static String[][] optionVariants(String[] options) { + if ((null == options) || (0 == options.length)) { + return new String[][] { new String[0]}; + } + // be nice, don't stomp input + String[] temp = new String[options.length]; + System.arraycopy(options, 0, temp, 0, temp.length); + options = temp; + boolean[] dup = new boolean[options.length]; + int numDups = 0; + + for (int i = 0; i < options.length; i++) { + String option = options[i]; + if (LangUtil.isEmpty(option)) { + throw new IllegalArgumentException("empty option at " + i); + } + if (option.endsWith("-")) { + options[i] = option.substring(0, option.length()-1); + dup[i] = true; + numDups++; + } + } + final String[] NONE = new String[0]; + final int variants = exp(2, numDups); + final String[][] result = new String[variants][]; + // variant is a bitmap wrt doing extra value when dup[k]=true + for (int variant = 0; variant < variants; variant++) { + ArrayList next = new ArrayList(); + int nextOption = 0; + for (int k = 0; k < options.length; k++) { + if (!dup[k] || (0 != (variant & (1 << (nextOption++))))) { + next.add(options[k]); + } + } + result[variant] = (String[]) next.toArray(NONE); + } + return result; + } + + private static int exp(int base, int power) { // not in Math? + if (0 > power) { + throw new IllegalArgumentException("negative power: " + power); + } + int result = 1; + while (0 < power--) { + result *= base; + } + return result; + } + + /** + * Make a copy of the array. + * @return an array with the same component type as source + * containing same elements, even if null. + * @throws IllegalArgumentException if source is null + */ + public static final Object[] copy(Object[] source) { + LangUtil.throwIaxIfNull(source, "source"); + final Class c = source.getClass().getComponentType(); + Object[] result = (Object[]) Array.newInstance(c, source.length); + System.arraycopy(source, 0, result, 0, result.length); + return result; + } + + + /** + * Convert arrays safely. The number of elements in the result + * will be 1 smaller for each element that is null or not assignable. + * This will use sink if it has exactly the right size. + * The result will always have the same component type as sink. + * @return an array with the same component type as sink + * containing any assignable elements in source (in the same order). + * @throws IllegalArgumentException if either is null + */ + public static Object[] safeCopy(Object[] source, Object[] sink) { + final Class sinkType = (null == sink + ? Object.class + : sink.getClass().getComponentType()); + final int sourceLength = (null == source ? 0 : source.length); + final int sinkLength = (null == sink ? 0 : sink.length); + + final int resultSize; + ArrayList result = null; + if (0 == sourceLength) { + resultSize = 0; + } else { + result = new ArrayList(sourceLength); + for (int i = 0; i < sourceLength; i++) { + if ((null != source[i]) + && (sinkType.isAssignableFrom(source[i].getClass()))) { + result.add(source[i]); + } + } + resultSize = result.size(); + } + if (resultSize != sinkLength) { + sink = (Object[]) Array.newInstance(sinkType, result.size()); + } + if (0 < resultSize) { + sink = result.toArray(sink); + } + return sink; + } + + /** + * @return a String with the unqualified class name of the class (or "null") + */ + public static String unqualifiedClassName(Class c) { + if (null == c) { + return "null"; + } + String name = c.getName(); + int loc = name.lastIndexOf("."); + if (-1 != loc) { + name = name.substring(1 + loc); + } + return name; + } + + /** + * @return a String with the unqualified class name of the object (or "null") + */ + public static String unqualifiedClassName(Object o) { + return LangUtil.unqualifiedClassName(null == o ? null : o.getClass()); + } + + /** render i right-justified with a given width less than about 40 */ + public static String toSizedString(long i, int width) { + String result = "" + i; + int size = result.length(); + if (width > size) { + final String pad = " "; + final int padLength = pad.length(); + if (width > padLength) { + width = padLength; + } + int topad = width-size; + result = pad.substring(0, topad) + result; + } + return result; + } + + /** clip StringBuffer to maximum number of lines */ + static String clipBuffer(StringBuffer buffer, int maxLines) { + if ((null == buffer) || (1 > buffer.length())) return ""; + StringBuffer result = new StringBuffer(); + int j = 0; + final int MAX = maxLines; + final int N = buffer.length(); + for (int i = 0, srcBegin = 0; i < MAX; srcBegin += j) { + // todo: replace with String variant if/since getting char? + char[] chars = new char[128]; + int srcEnd = srcBegin+chars.length; + if (srcEnd >= N) { + srcEnd = N-1; + } + if (srcBegin == srcEnd) break; + //log("srcBegin:" + srcBegin + ":srcEnd:" + srcEnd); + buffer.getChars(srcBegin, srcEnd, chars, 0); + for (j = 0; j < srcEnd-srcBegin/*chars.length*/; j++) { + char c = chars[j]; + if (c == '\n') { + i++; + j++; + break; + } + } + try { result.append(chars, 0, j); } + catch (Throwable t) { } + } + return result.toString(); + } + + /** + * @return "({UnqualifiedExceptionClass}) {message}" + */ + public static String renderExceptionShort(Throwable e) { + if (null == e) + return "(Throwable) null"; + return "(" + LangUtil.unqualifiedClassName(e) + ") " + e.getMessage(); + } + + /** + * Renders exception <code>t</code> after unwrapping and + * eliding any test packages. + * @param t <code>Throwable</code> to print. + * @see #maxStackTrace + */ + public static String renderException(Throwable t) { + return renderException(t, true); + } + + /** + * Renders exception <code>t</code>, unwrapping, + * optionally eliding and limiting total number of lines. + * @param t <code>Throwable</code> to print. + * @param elide true to limit to 100 lines and elide test packages + * @see StringChecker#TEST_PACKAGES + */ + public static String renderException(Throwable t, boolean elide) { + if (null == t) return "null throwable"; + t = unwrapException(t); + StringBuffer stack = stackToString(t, false); + if (elide) { + elideEndingLines(StringChecker.TEST_PACKAGES, stack, 100); + } + return stack.toString(); + } + + /** + * Trim ending lines from a StringBuffer, + * clipping to maxLines and further removing any number of + * trailing lines accepted by checker. + * @param checker returns true if trailing line should be elided. + * @param stack StringBuffer with lines to elide + * @param maxLines int for maximum number of resulting lines + */ + static void elideEndingLines(StringChecker checker, StringBuffer stack, int maxLines) { + if (null == checker || (null == stack) || (0 == stack.length())) { + return; + } + final LinkedList lines = new LinkedList(); + StringTokenizer st = new StringTokenizer(stack.toString(),"\n\r"); + while (st.hasMoreTokens() && (0 < --maxLines)) { + lines.add(st.nextToken()); + } + st = null; + + String line; + int elided = 0; + while (!lines.isEmpty()) { + line = (String) lines.getLast(); + if (!checker.acceptString(line)) { + break; + } else { + elided++; + lines.removeLast(); + } + } + if ((elided > 0) || (maxLines < 1)) { + final int EOL_LEN = EOL.length(); + int totalLength = 0; + while (!lines.isEmpty()) { + totalLength += EOL_LEN + ((String) lines.getFirst()).length(); + lines.removeFirst(); + } + if (stack.length() > totalLength) { + stack.setLength(totalLength); + if (elided > 0) { + stack.append(" (... " + elided + " lines...)"); + } + } + } + } + + + /** Dump message and stack to StringBuffer. */ + public static StringBuffer stackToString(Throwable throwable, boolean skipMessage) { + if (null == throwable) { + return new StringBuffer(); + } + StringWriter buf = new StringWriter(); + PrintWriter writer = new PrintWriter(buf); + if (!skipMessage) { + writer.println(throwable.getMessage()); + } + throwable.printStackTrace(writer); + try { buf.close(); } + catch (IOException ioe) {} // ignored + return buf.getBuffer(); + } + + + /** @return Throwable input or tail of any wrapped exception chain */ + public static Throwable unwrapException(Throwable t) { + if (t instanceof InvocationTargetException) { + Throwable thrown = ((InvocationTargetException) t).getTargetException(); + if (null != thrown) { + return unwrapException(thrown); + } + } else if (t instanceof ClassNotFoundException) { + Throwable thrown = ((ClassNotFoundException) t).getException(); + if (null != thrown) { + return unwrapException(thrown); + } + } + // ChainedException + // ExceptionInInitializerError + return t; + } + + /** + * Replacement for Arrays.asList(..) which gacks on null + * and returns a List in which remove is an unsupported operation. + * @param array the Object[] to convert (may be null) + * @return the List corresponding to array (never null) + */ + public static List arrayAsList(Object[] array) { + if ((null == array) || (1 > array.length)) { + return Collections.EMPTY_LIST; + } + ArrayList list = new ArrayList(); + list.addAll(Arrays.asList(array)); + return list; + } + + + + + /** check if input contains any packages to elide. */ + public static class StringChecker { + static StringChecker TEST_PACKAGES = new StringChecker( new String[] + { "org.aspectj.testing.", + "org.eclipse.jdt.internal.junit", + "junit.framework.", + "org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner" + }); + + String[] infixes; + + /** @param infixes adopted */ + StringChecker(String[] infixes) { + this.infixes = infixes; + } + + /** @return true if input contains infixes */ + public boolean acceptString(String input) { + boolean result = false; + if (!LangUtil.isEmpty(input)) { + for (int i = 0; !result && (i < infixes.length); i++) { + result = (-1 != input.indexOf(infixes[i])); + } + } + return result; + } + } +} diff --git a/util/src/org/aspectj/util/LineReader.java b/util/src/org/aspectj/util/LineReader.java new file mode 100644 index 000000000..622d7b4c9 --- /dev/null +++ b/util/src/org/aspectj/util/LineReader.java @@ -0,0 +1,212 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.util; + +import java.io.*; +import java.util.ArrayList; + +/** + * LineNumberReader which absorbs comments and blank lines + * and renders as file:line + */ +public class LineReader extends LineNumberReader { + /** delimited multi-line output of readToBlankLine */ + public static final String RETURN= "\n\r"; + + private static final String[] NONE = new String[0]; + private static final String cSCRIPT = "#"; + private static final String cJAVA = "//"; + private static final String[] TESTER_LEAD = new String[] {cSCRIPT, cJAVA}; + + /** + * Convenience factory for tester suite files + * @return null if IOException or IllegalArgumentException thrown + */ + public static final LineReader createTester(File file) { + return create(file, TESTER_LEAD, null); + } + + /** + * convenience factory + * @return null if IOException or IllegalArgumentException thrown + */ + public static final LineReader create(File file, + String[] leadComments, String[] eolComments) { + try { + FileReader reader = new FileReader(file); + return new LineReader(reader, file, leadComments, eolComments); + } catch (IllegalArgumentException e) { + } catch (IOException e) { + } + return null; + } + + final private File file; + final private String[] eolComments; + final private String[] leadComments; + transient String lastLine; + + /** + * @param file the File used to open the FileReader + * @param leadComments the String[] to be taken as the start of + * comments when they are the first non-blank text on a line - + * pass null to signal none. + * @param leadComments the String[] to be taken as the start of + * comment anywhere on a line - pass null to signal none. + *@throws IllegalArgumentException if any String in + * leadComments or eolComments is null. + */ + public LineReader(FileReader reader, File file, + String[] leadComments, String[] eolComments) { + super(reader); + this.file = file; + this.eolComments = normalize(eolComments); + this.leadComments = normalize(leadComments); + } + public LineReader(FileReader reader, File file) { + this(reader, file, null, null); + } + + /** @return file:line */ + public String toString() { + return file.getPath() + ":" + getLineNumber(); + } + + /** @return underlying file */ + public File getFile() { return file; } + + /** + * Reader first..last (inclusive) and return in String[]. + * This will return (1+(last-first)) elements only if this + * reader has not read past the first line and there are last lines + * and there are no IOExceptions during reads. + * @param first the first line to read - if negative, use 0 + * @param last the last line to read (inclusive) + * - if less than first, use first + * @return String[] of first..last (inclusive) lines read or + */ + public String[] readLines(int first, int last) { + if (0 > first) first = 0; + if (first > last) last = first; + ArrayList list = new ArrayList(); + try { + String line = null; + while (getLineNumber() < first) { + line = readLine(); + if (null == line) { + break; + } + } + if (getLineNumber() > first) { + // XXX warn? something else read past line + } + if ((null != line) && (first == getLineNumber())) { + list.add(line); + while (last >= getLineNumber()) { + line = readLine(); + if (null == line) { + break; + } + list.add(line); + } + } + } catch (IOException e) { + return NONE; + } + return (String[]) list.toArray(NONE); + } + + /** Skip to next blank line + * @return the String containing all lines skipped (delimited with RETURN) + */ + public String readToBlankLine() throws IOException { + StringBuffer sb = new StringBuffer(); + String input; + while (null != (input = nextLine(false))) { // get next empty line to restart + sb.append(input); + sb.append(RETURN);// XXX verify/ignore/correct + } + return sb.toString(); + } + + /** + * lastLine is set only by readClippedLine, not readLine. + * @return the last line read, after clipping + */ + public String lastLine() { + return lastLine; + } + + /** + * Get the next line from the input stream, stripping eol and + * leading comments. + * If emptyLinesOk is true, then this reads past lines which are + * empty after omitting comments and trimming until the next non-empty line. + * Otherwise, this returns null on reading an empty line. + * (The input stream is not exhausted until this + * returns null when emptyLines is true.) + * @param skipEmpties if true, run to next non-empty line; if false, return next line + * @return null if no more lines or got an empty line when they are not ok, + * or next non-null, non-empty line in reader, + * ignoring comments + */ + public String nextLine(boolean skipEmpties) throws IOException { + String result; + do { + result = readClippedLine(); + if ((null != result) && skipEmpties && (0 == result.length())) { + continue; + } + return result; + } while (true); + } + + /** @return null if no more lines or a clipped line otherwise */ + protected String readClippedLine() throws IOException { + String result = readLine(); + if (result != null) { + result = result.trim(); + int len = result.length(); + for (int i = 0; ((0 < len) && (i < leadComments.length)); i++) { + if (result.startsWith(leadComments[i])) { + result = ""; + len = 0; + } + } + for (int i = 0; ((0 < len) && (i < eolComments.length)); i++) { + int loc = result.indexOf(eolComments[i]); + if (-1 != loc) { + result = result.substring(0, loc); + len = result.length(); + } + } + } + lastLine = result; + return result; + } + + private String[] normalize(String[] input) { + if ((null == input) || (0 == input.length)) return NONE; + String[] result = new String[input.length]; + System.arraycopy(input, 0, result, 0, result.length); + for (int i = 0; i < result.length; i++) { + if ((null == result[i]) || (0 == result[i].length())) { + throw new IllegalArgumentException("empty input at [" + i + "]"); + } + } + return result; + } +} + diff --git a/util/src/org/aspectj/util/NonLocalExit.java b/util/src/org/aspectj/util/NonLocalExit.java new file mode 100644 index 000000000..1c75d075d --- /dev/null +++ b/util/src/org/aspectj/util/NonLocalExit.java @@ -0,0 +1,39 @@ +/* ******************************************************************* + * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.util; + +/** + * Throw this when a non-local exit is required (suggested for tests only). + */ +public class NonLocalExit extends RuntimeException { + + public static final int SUCCEESS = 0; + public static final int FAULURE = 1; + + private int exitCode; + + public NonLocalExit(int exitCode) { + this(); + this.exitCode = exitCode; + } + + public NonLocalExit() { + super(); + } + + public int getExitCode() { + return exitCode; + } + +} diff --git a/util/src/org/aspectj/util/PartialOrder.java b/util/src/org/aspectj/util/PartialOrder.java new file mode 100644 index 000000000..6051caffd --- /dev/null +++ b/util/src/org/aspectj/util/PartialOrder.java @@ -0,0 +1,213 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.util; + +import java.util.*; + + +/** This class implements a partial order + * + * It includes routines for doing a topo-sort + */ + +public class PartialOrder { + + /** All classes that want to be part of a partial order must implement + * PartialOrder.PartialComparable. + */ + public static interface PartialComparable { + /** + * @returns <ul> + * <li>+1 if this is greater than other</li> + * <li>-1 if this is less than other</li> + * <li>0 if this is not comparable to other</li> + * </ul> + * + * <b> Note: returning 0 from this method doesn't mean the same thing as returning + * 0 from java.util.Comparable.compareTo()</b> + */ + public int compareTo(Object other); + + /** + * This method can provide a deterministic ordering for elements that + * are strictly not comparable. If you have no need for this, this method + * can just return 0 whenever called. + */ + public int fallbackCompareTo(Object other); + } + + private static class SortObject { + PartialComparable object; + List/*SortObject*/ smallerObjects = new LinkedList(); + List/*SortObject*/ biggerObjects = new LinkedList(); + + public SortObject(PartialComparable o) { + object = o; + } + + boolean hasNoSmallerObjects() { return smallerObjects.size() == 0; } + + boolean removeSmallerObject(SortObject o) { + smallerObjects.remove(o); + return hasNoSmallerObjects(); + } + + void addDirectedLinks(SortObject other) { + int cmp = object.compareTo(other.object); + if (cmp == 0) return; + if (cmp > 0) { + this.smallerObjects.add(other); + other.biggerObjects.add(this); + } else { + this.biggerObjects.add(other); + other.smallerObjects.add(this); + } + } + + public String toString() { + return object.toString(); //+smallerObjects+biggerObjects; + } + } + + private static void addNewPartialComparable(List graph, PartialComparable o) { + SortObject so = new SortObject(o); + for (Iterator i = graph.iterator(); i.hasNext(); ) { + SortObject other = (SortObject)i.next(); + so.addDirectedLinks(other); + } + graph.add(so); + } + + private static void removeFromGraph(List graph, SortObject o) { + for (Iterator i = graph.iterator(); i.hasNext(); ) { + SortObject other = (SortObject)i.next(); + + if (o == other) i.remove(); + //??? could use this to build up a new queue of objects with no + //??? smaller ones + other.removeSmallerObject(o); + } + } + + /** + * @param objects must all implement PartialComparable + * + * @returns the same members as objects, but sorted according to their partial + * order. returns null if the objects are cyclical + * + */ + public static List sort(List objects) { + // lists of size 0 or 1 don't need any sorting + if (objects.size() < 2) return objects; + + //??? we might want to optimize a few other cases of small size + + //??? I don't like creating this data structure, but it does give good + //??? separation of concerns. + List sortList = new LinkedList(); //objects.size()); + for (Iterator i = objects.iterator(); i.hasNext(); ) { + addNewPartialComparable(sortList, (PartialComparable)i.next()); + } + + //System.out.println(sortList); + + // now we have built our directed graph + // use a simple sort algorithm from here + // can increase efficiency later + // List ret = new ArrayList(objects.size()); + final int N = objects.size(); + for (int index = 0; index < N; index++) { + //System.out.println(sortList); + //System.out.println("-->" + ret); + + SortObject leastWithNoSmallers = null; + + for (Iterator i = sortList.iterator(); i.hasNext(); ) { + SortObject so = (SortObject)i.next(); + //System.out.println(so); + if (so.hasNoSmallerObjects()) { + if (leastWithNoSmallers == null || + so.object.fallbackCompareTo(leastWithNoSmallers.object) < 0) + { + leastWithNoSmallers = so; + } + } + } + + if (leastWithNoSmallers == null) return null; + + removeFromGraph(sortList, leastWithNoSmallers); + objects.set(index, leastWithNoSmallers.object); + } + + return objects; + } + + /*********************************************************************************** + /* a minimal testing harness + ***********************************************************************************/ + static class Token implements PartialComparable { + private String s; + + Token(String s) { + this.s = s; + } + + public int compareTo(Object other) { + Token t = (Token)other; + + int cmp = s.charAt(0) - t.s.charAt(0); + if (cmp == 1) return 1; + if (cmp == -1) return -1; + return 0; + } + + public int fallbackCompareTo(Object other) { + return -s.compareTo( ((Token)other).s ); + } + + public String toString() { + return s; + } + } + + public static void main(String[] args) { + List l = new ArrayList(); + l.add(new Token("a1")); + l.add(new Token("c2")); + l.add(new Token("b3")); + l.add(new Token("f4")); + l.add(new Token("e5")); + l.add(new Token("d6")); + l.add(new Token("c7")); + l.add(new Token("b8")); + + l.add(new Token("z")); + l.add(new Token("x")); + + l.add(new Token("f9")); + l.add(new Token("e10")); + l.add(new Token("a11")); + l.add(new Token("d12")); + l.add(new Token("b13")); + l.add(new Token("c14")); + + System.out.println(l); + + sort(l); + + System.out.println(l); + } +} diff --git a/util/src/org/aspectj/util/Reflection.java b/util/src/org/aspectj/util/Reflection.java new file mode 100644 index 000000000..bee7bd70e --- /dev/null +++ b/util/src/org/aspectj/util/Reflection.java @@ -0,0 +1,106 @@ +/* ******************************************************************* + * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.util; + +import java.lang.reflect.*; +import java.lang.reflect.Method; + +public class Reflection { + + private Reflection() { + } + + public static Object invokestatic(Class class_, String name) { + return invokestaticN(class_, name, new Object[0]); + } + + public static Object invokestatic(Class class_, String name, Object arg1) { + return invokestaticN(class_, name, new Object[] { arg1 }); + } + + public static Object invokestatic(Class class_, String name, Object arg1, Object arg2) { + return invokestaticN(class_, name, new Object[] { arg1, arg2 }); + } + + public static Object invokestatic(Class class_, String name, Object arg1, Object arg2, Object arg3) { + return invokestaticN(class_, name, new Object[] { arg1, arg2, arg3 }); + } + + + public static Object invokestaticN(Class class_, String name, Object[] args) { + return invokeN(class_, name, null, args); + } + + + public static Object invoke(Class class_, Object target, String name, Object arg1) { + return invokeN(class_, name, target, new Object[] { arg1 }); + } + + public static Object invoke(Class class_, Object target, String name, Object arg1, Object arg2) { + return invokeN(class_, name, target, new Object[] { arg1, arg2 }); + } + + public static Object invoke(Class class_, Object target, String name, Object arg1, Object arg2, Object arg3) { + return invokeN(class_, name, target, new Object[] { arg1, arg2, arg3 }); + } + + + + + public static Object invokeN(Class class_, String name, Object target, Object[] args) { + Method meth = getMatchingMethod(class_, name, args); + try { + return meth.invoke(target, args); + } catch (IllegalAccessException e) { + throw new RuntimeException(e.toString()); + } catch (InvocationTargetException e) { + Throwable t = e.getTargetException(); + if (t instanceof Error) throw (Error)t; + if (t instanceof RuntimeException) throw (RuntimeException)t; + t.printStackTrace(); + throw new RuntimeException(t.toString()); + } + } + + + public static Method getMatchingMethod(Class class_, String name, Object[] args) { + Method[] meths = class_.getMethods(); + for (int i=0; i < meths.length; i++) { + Method meth = meths[i]; + if (meth.getName().equals(name) && isCompatible(meth, args)) { + return meth; + } + } + return null; + } + + private static boolean isCompatible(Method meth, Object[] args) { + // ignore methods with overloading other than lengths + return meth.getParameterTypes().length == args.length; + } + + + + + public static Object getStaticField(Class class_, String name) { + try { + return class_.getField(name).get(null); + } catch (IllegalAccessException e) { + throw new RuntimeException("unimplemented"); + } catch (NoSuchFieldException e) { + throw new RuntimeException("unimplemented"); + } + } + +} diff --git a/util/src/org/aspectj/util/StreamPrintWriter.java b/util/src/org/aspectj/util/StreamPrintWriter.java new file mode 100644 index 000000000..6167e6694 --- /dev/null +++ b/util/src/org/aspectj/util/StreamPrintWriter.java @@ -0,0 +1,101 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.util; + +import java.io.*; + +/** + * Used for writing converting text written to an output stream into + * a string. Deprecated - use StringWriter: + * <pre> + * StringWriter sw = new StringWriter(); + * PrintWriter pw = new PrintWriter(sw, true); + * ... write to pw + * String result = sw.getBuffer().toString(); + * </pre> + * @deprecated use StringWriter to construct PrintWriter + * @author Mik Kersten + */ +public class StreamPrintWriter extends PrintWriter { + private String contents = ""; + + public StreamPrintWriter(Writer out) { + super(out); + } + + public String getContents() { + return contents; + } + + public void flushBuffer() { + contents = ""; + super.flush(); + } + + public void print(char x) { + contents += x + "\n"; + } + + public void print(char[] x) { + contents += new String( x ); + } + + public void print(int x) { + contents += x; + } + + public void print(String x) { + contents += x; + } + + public void println(char x) { + contents += x + "\n"; + } + + public void println(char[] x) { + contents += new String( x ) + "\n"; + } + + public void println(int x) { + contents += x + "\n"; + } + + public void println(String x) { + contents += x + "\n"; + } + + public void write( byte[] x ) { + contents += new String( x ); + } + + public void write( byte[] x, int i1, int i2 ) { + StringWriter writer = new StringWriter(); + String s = new String( x ); + writer.write( s.toCharArray(), i1, i2 ); + contents += writer.getBuffer().toString(); + } + + public void write( int c ) { + contents += c; + } + + public void write( String s ) { + contents += s; + } + + public void write( String s, int i1, int i2 ) { + contents += s; + } +} diff --git a/util/src/org/aspectj/util/TypeSafeEnum.java b/util/src/org/aspectj/util/TypeSafeEnum.java new file mode 100644 index 000000000..6980d39c9 --- /dev/null +++ b/util/src/org/aspectj/util/TypeSafeEnum.java @@ -0,0 +1,38 @@ +/* ******************************************************************* + * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.util; + +import java.io.*; + +public class TypeSafeEnum { + private byte key; + private String name; + + public TypeSafeEnum(String name, int key) { + this.name = name; + if (key > Byte.MAX_VALUE || key < Byte.MIN_VALUE) { + throw new IllegalArgumentException("key doesn't fit into a byte: " + key); + } + this.key = (byte)key; + } + + public String toString() { return name; } + + public String getName() { return name; } + public byte getKey() { return key; } + + public void write(DataOutputStream s) throws IOException { + s.writeByte(key); + } +} diff --git a/util/testsrc/UtilModuleTests.java b/util/testsrc/UtilModuleTests.java new file mode 100644 index 000000000..ddb1da5f5 --- /dev/null +++ b/util/testsrc/UtilModuleTests.java @@ -0,0 +1,30 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + + +// default package + +import junit.framework.*; +import junit.framework.Test; + +public class UtilModuleTests extends TestCase { + + public static Test suite() { + TestSuite suite = new TestSuite(UtilModuleTests.class.getName()); + suite.addTest(org.aspectj.util.UtilTests.suite()); + return suite; + } + + public UtilModuleTests(String name) { super(name); } + +} diff --git a/util/testsrc/org/aspectj/util/FileUtilTest.java b/util/testsrc/org/aspectj/util/FileUtilTest.java new file mode 100644 index 000000000..7cda1692c --- /dev/null +++ b/util/testsrc/org/aspectj/util/FileUtilTest.java @@ -0,0 +1,377 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + +package org.aspectj.util; + +import java.io.EOFException; +import java.io.File; +import java.io.FilenameFilter; +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintStream; +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; + +import junit.framework.TestCase; +import junit.textui.TestRunner; + +/** + * + */ +public class FileUtilTest extends TestCase { + public static final String[] NONE = new String[0]; + + boolean log = false; + + /** List of File files or directories to delete when exiting */ + final ArrayList tempFiles; + public FileUtilTest(String s) { + super(s); + tempFiles = new ArrayList(); + } + + public static void main(String[] args) { + TestRunner.main(new String[] {"org.aspectj.util.FileUtilUT"}); + } + + public void tearDown() { + for (ListIterator iter = tempFiles.listIterator(); iter.hasNext();) { + File dir = (File) iter.next(); + FileUtil.deleteContents(dir); + dir.delete(); + iter.remove(); + } + } + + public void testDirCopySubdirs() throws IOException { // XXX dir diff + File srcDir = new File("src"); + File destDir = FileUtil.getTempDir("testDirCopySubdirs"); + FileUtil.copyDir(srcDir, destDir); + FileUtil.deleteContents(destDir); + destDir.delete(); + } + + public void testDirCopySubdirsSuffix() throws IOException { // XXX dir diff + File srcDir = new File("src"); + File destDir = FileUtil.getTempDir("testDirCopySubdirsSuffix"); + FileUtil.copyDir(srcDir, destDir, ".java", ".aj"); + + FileUtil.deleteContents(destDir); + destDir.delete(); + } + + public void testGetURL() { + String[] args = new String[] + {".", "../util/testdata", "../lib/test/aspectjrt.jar" }; + for (int i = 0; i < args.length; i++) { + checkGetURL(args[i]); + } + } + + /** + * Method checkGetURL. + * @param string + * @param uRL + */ + private void checkGetURL(String arg) { + assertTrue(null != arg); + File f = new File(arg); + assertTrue(null != f); + URL url = FileUtil.getFileURL(f); + assertTrue(null != url); + log("url " + url); + if (!f.exists()) { + log("not exist " + f); + } else if (f.isDirectory()) { + log("directory " + f); + } else { + log(" file " + f); + InputStream in = null; + try { + in = url.openStream(); + } catch (IOException e) { + assertTrue("IOException: " + e, false); + } finally { + if (null != in) { + try { in.close(); } + catch (IOException e) {} + } + } + } + } + + + public void testGetTempDir() { + boolean pass = true; + boolean delete = true; + checkGetTempDir("parent", null, pass, delete); + checkGetTempDir(null, "child", pass, delete); + tempFiles.add(checkGetTempDir("parent", "child", pass, !delete)); + tempFiles.add(checkGetTempDir("parent", "child", pass, !delete)); + tempFiles.add(checkGetTempDir("parent", "child", pass, !delete)); + } + + File checkGetTempDir(String parent, String child, boolean ok, boolean delete) { + File parentDir = FileUtil.getTempDir(parent); + assertTrue("unable to create " + parent, null != parentDir); + File dir = FileUtil.makeNewChildDir(parentDir, child); + log("parent=" + parent + " child=" + child + " -> " + dir); + assertTrue("dir: " + dir, ok == (dir.canWrite() && dir.isDirectory())); + if (delete) { + dir.delete(); + } + return dir; + } + + public void testRandomFileString() { + ArrayList results = new ArrayList(); + for (int i = 0; i < 1000; i++) { + String s = FileUtil.randomFileString(); + if (results.contains(s)) { + System.err.println("warning: got duplicate at iteration " + i); + } + results.add(s); +// System.err.print(" " + s); +// if (0 == (i % 5)) { +// System.err.println(""); +// } + } + } + + public void testNormalizedPath() { + File tempFile = null; + try { + tempFile = File.createTempFile("FileUtilTest", "tmp"); + } catch (IOException e) { + System.err.println("aborting test - unable to create temp file"); + } + File parentDir = tempFile.getParentFile(); + String tempFilePath = FileUtil.normalizedPath(tempFile, parentDir); + assertEquals(tempFile.getName(), tempFilePath); + } + + public void testFileToClassName() { + + File basedir = new File("/base/dir"); + File classFile = new File(basedir, "foo/Bar.class"); + assertEquals("foo.Bar", FileUtil.fileToClassName(basedir, classFile)); + + classFile = new File(basedir, "foo\\Bar.class"); + assertEquals("foo.Bar", FileUtil.fileToClassName(basedir, classFile)); + + assertEquals("Bar", FileUtil.fileToClassName(null, classFile)); + + classFile = new File("/home/classes/org/aspectj/lang/JoinPoint.class"); + assertEquals("org.aspectj.lang.JoinPoint", FileUtil.fileToClassName(null, classFile)); + + classFile = new File("/home/classes/com/sun/tools/Javac.class"); + assertEquals("com.sun.tools.Javac", FileUtil.fileToClassName(null, classFile)); + } + + public void testDeleteContents() { + File f = new File("testdata/foo"); + f.mkdirs(); + File g = new File(f, "bar"); + g.mkdirs(); + File h = new File(g, "bash"); + h.mkdirs(); + int d = FileUtil.deleteContents(f); + assertTrue(0 == d); + assertTrue(0 == f.list().length); + f.delete(); + assertTrue(!f.exists()); + } + + public void testLineSeek() { + String path = "testdata/testLineSeek"; + File file = new File(path); + path = file.getPath(); + String contents = "0123456789" + LangUtil.EOL; + contents += contents; + FileUtil.writeAsString(file, contents); + tempFiles.add(file); + List sourceList = new ArrayList(); + sourceList.add(file.getPath()); + + final ArrayList errors = new ArrayList(); + final PrintStream errorSink = new PrintStream(System.err, true) { + public void println(String error) { + errors.add(error); + } + }; + for (int i = 0; i < 10; i++) { + List result = FileUtil.lineSeek(""+i, sourceList, true, errorSink); + assertEquals(2, result.size()); + assertEquals(path + ":1:" + i, result.get(0)); + assertEquals(path + ":2:" + i, result.get(1)); + if (!LangUtil.isEmpty(errors)) { // XXX prefer fast-fail? + assertTrue("errors: " + errors, false); + } + } + + } + public void testLineSeekMore() { + final int MAX = 3; // 1..10 + final String prefix = new File("testdata/testLineSeek").getPath(); + // setup files 0..MAX with 2*MAX lines + String[] sources = new String[MAX]; + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < sources.length; i++) { + sources[i] = new File(prefix + i).getPath(); + sb.append("not matched"); + sb.append(LangUtil.EOL); + sb.append("0123456789"); + sb.append(LangUtil.EOL); + } + final String contents = sb.toString(); + for (int i = 0; i < sources.length; i++) { + File file = new File(sources[i]); + FileUtil.writeAsString(file, contents); + tempFiles.add(file); + } + // now test + final ArrayList errors = new ArrayList(); + final PrintStream errorSink = new PrintStream(System.err, true) { + public void println(String error) { + errors.add(error); + } + }; + List sourceList = new ArrayList(); + sourceList.addAll(Arrays.asList(sources)); + sourceList = Collections.unmodifiableList(sourceList); + for (int k = 0; k < sources.length; k++) { + List result = FileUtil.lineSeek(""+k, sourceList, true, errorSink); + // number k found in every other line of every file at index k + Iterator iter = result.iterator(); + for (int i = 0; i < MAX; i++) { // for each file + for (int j = 1; j < (MAX+1); j++) { // for every other line + assertTrue(iter.hasNext()); + assertEquals(prefix + i + ":" + 2*j + ":" + k, iter.next()); + } + } + if (!LangUtil.isEmpty(errors)) { // XXX prefer fast-fail? + assertTrue("errors: " + errors, false); + } + } + } + + public void testDirCopyNoSubdirs() throws IOException { + String[] srcFiles = new String[] { "one.java", "two.java", "three.java"}; + String[] destFiles = new String[] { "three.java", "four.java", "five.java" }; + String[] allFiles = new String[] + { "one.java", "two.java", "three.java", "four.java", "five.java" }; + File srcDir = makeDir("FileUtilUT_srcDir", srcFiles); + File destDir = makeDir("FileUtilUT_destDir", destFiles); + assertTrue(null != srcDir); + assertTrue(null != destDir); + assertTrue(NONE == dirContains(srcDir, srcFiles)); + assertTrue(NONE == dirContains(destDir, destFiles)); + + FileUtil.copyDir(srcDir, destDir); + String[] resultOne = dirContains(destDir, allFiles); + FileUtil.copyDir(srcDir, destDir); + String[] resultTwo = dirContains(destDir, allFiles); + + FileUtil.deleteContents(srcDir); + FileUtil.deleteContents(destDir); + srcDir.delete(); + destDir.delete(); + + assertTrue(NONE == resultOne); + assertTrue(NONE == resultTwo); + } + + public void testDirCopyNoSubdirsWithSuffixes() throws IOException { + String[] srcFiles = new String[] { "one.java", "two.java", "three.java"}; + String[] destFiles = new String[] { "three.java", "four.java", "five.java" }; + String[] allFiles = new String[] + { "one.aj", "two.aj", "three.aj", "three.java", "four.java", "five.java" }; + File srcDir = makeDir("FileUtilUT_srcDir", srcFiles); + File destDir = makeDir("FileUtilUT_destDir", destFiles); + assertTrue(null != srcDir); + assertTrue(null != destDir); + assertTrue(NONE == dirContains(srcDir, srcFiles)); + assertTrue(NONE == dirContains(destDir, destFiles)); + + FileUtil.copyDir(srcDir, destDir, ".java", ".aj"); + String[] resultOne = dirContains(destDir, allFiles); + FileUtil.copyDir(srcDir, destDir, ".java", ".aj"); + String[] resultTwo = dirContains(destDir, allFiles); + + FileUtil.deleteContents(srcDir); + FileUtil.deleteContents(destDir); + srcDir.delete(); + destDir.delete(); + + assertTrue(NONE == resultOne); + assertTrue(NONE == resultTwo); + } + + public void testDirCopySubdirsSuffixRoundTrip() throws IOException { + final File srcDir = new File("src"); + final File one = FileUtil.getTempDir("testDirCopySubdirsSuffixRoundTrip_1"); + final File two = FileUtil.getTempDir("testDirCopySubdirsSuffixRoundTrip_2"); + FileUtil.copyDir(srcDir, one); // no selection + FileUtil.copyDir(two, one, ".java", ".aj"); // only .java files + FileUtil.copyDir(one, two, ".aj", ".java"); + + FileUtil.deleteContents(one); + one.delete(); + FileUtil.deleteContents(two); + two.delete(); + } + + /** + * Verify that dir contains files, + * and return the names of other files in dir. + * @return the contents of dir after excluding files + * or NONE if none + * @throws AssertionFailedError if any files are not in dir + */ + String[] dirContains(File dir, final String[] files) { + final ArrayList toFind = new ArrayList(Arrays.asList(files)); + FilenameFilter filter = new FilenameFilter() { + public boolean accept(File d, String name) { + return !toFind.remove(name); + } + }; + String[] result = dir.list(filter); + if (0 < toFind.size()) { + assertTrue(""+toFind, false); + } + return (result.length == 0 ? NONE : result); + } + + File makeDir(String loc, String[] files) throws IOException { + File d = new File(loc); + d.mkdirs(); + assertTrue(d.exists()); + assertTrue(d.canWrite()); + for (int i = 0; i < files.length; i++) { + File f = new File(d, files[i]); + assertTrue(files[i], f.createNewFile()); + } + return d; + } + + private void log(String s) { + if (log) { + System.err.println(s); + } + } +} diff --git a/util/testsrc/org/aspectj/util/LangUtilTest.java b/util/testsrc/org/aspectj/util/LangUtilTest.java new file mode 100644 index 000000000..98aab88ba --- /dev/null +++ b/util/testsrc/org/aspectj/util/LangUtilTest.java @@ -0,0 +1,266 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + +package org.aspectj.util; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import junit.framework.TestCase; + +/** + * + */ +public class LangUtilTest extends TestCase { + + public LangUtilTest(String name) { + super(name); + } + + /** @see LangUtil.extractOptions(String[], String[], int[], List) */ + public void testExtractOptions() { + ArrayList extracted = new ArrayList(); + String[] args = new String[] { "-d", "classes", "-classpath", "foo.jar", "-verbose", "Bar.java" }; + String[] validOptions = new String[] { "-classpath", "-d", "-verbose", "-help" }; + int[] optionArgs = new int[] { 1, 1, 0, 0 }; + String[] result = LangUtil.extractOptions(args, validOptions, optionArgs, extracted); + String resultString = "" + Arrays.asList(result); + String EXP = "[Bar.java]"; + assertTrue(resultString + " != " + EXP, resultString.equals(EXP)); + EXP = "[-d, classes, -classpath, foo.jar, -verbose]"; + resultString = "" + extracted; + assertTrue(resultString + " != " + EXP, resultString.equals(EXP)); + + // no input, no output + extracted.clear(); + args = new String[] {}; + result = LangUtil.extractOptions(args, validOptions, optionArgs, extracted); + resultString = "" + Arrays.asList(result); + EXP = "[]"; + assertTrue(resultString + " != " + EXP, resultString.equals(EXP)); + resultString = "" + extracted; + assertTrue(resultString + " != " + EXP, resultString.equals(EXP)); + + // one input, nothing extracted + extracted.clear(); + args = new String[] {"Bar.java"}; + result = LangUtil.extractOptions(args, validOptions, optionArgs, extracted); + resultString = "" + Arrays.asList(result); + EXP = "[Bar.java]"; + assertTrue(resultString + " != " + EXP, resultString.equals(EXP)); + EXP = "[]"; + resultString = "" + extracted; + assertTrue(resultString + " != " + EXP, resultString.equals(EXP)); + + // one input, extracted + extracted.clear(); + args = new String[] {"-verbose"}; + result = LangUtil.extractOptions(args, validOptions, optionArgs, extracted); + resultString = "" + Arrays.asList(result); + EXP = "[]"; + assertTrue(resultString + " != " + EXP, resultString.equals(EXP)); + EXP = "[-verbose]"; + resultString = "" + extracted; + assertTrue(resultString + " != " + EXP, resultString.equals(EXP)); + + // ------- booleans + validOptions = new String[] { "-help", "-verbose" }; + optionArgs = null; + + // one input, extracted + extracted.clear(); + args = new String[] {"-verbose"}; + result = LangUtil.extractOptions(args, validOptions, optionArgs, extracted); + resultString = "" + Arrays.asList(result); + EXP = "[]"; + assertTrue(resultString + " != " + EXP, resultString.equals(EXP)); + EXP = "[-verbose]"; + resultString = "" + extracted; + assertTrue(resultString + " != " + EXP, resultString.equals(EXP)); + + // one input, not extracted + extracted.clear(); + args = new String[] {"Bar.java"}; + result = LangUtil.extractOptions(args, validOptions, optionArgs, extracted); + resultString = "" + Arrays.asList(result); + EXP = "[Bar.java]"; + assertTrue(resultString + " != " + EXP, resultString.equals(EXP)); + EXP = "[]"; + resultString = "" + extracted; + assertTrue(resultString + " != " + EXP, resultString.equals(EXP)); + } + + /** @see LangUtil.extractOptions(String[], String[][]) */ + public void testExtractOptionsArrayCollector() { + String[] args = new String[] { "-d", "classes", "-classpath", "foo.jar", "-verbose", "Bar.java" }; + String[][] OPTIONS = new String[][] { + new String[] {"-classpath", null }, + new String[] {"-d", null}, + new String[] {"-verbose"}, + new String[] { "-help" }}; + + String[][] options = LangUtil.copyStrings(OPTIONS); + + String[] result = LangUtil.extractOptions(args, options); + String resultString = "" + Arrays.asList(result); + String EXP = "[Bar.java]"; + assertTrue(resultString + " != " + EXP, resultString.equals(EXP)); + assertTrue("-verbose".equals(options[2][0])); + assertTrue("foo.jar".equals(options[0][1])); + assertTrue("classes".equals(options[1][1])); + assertTrue("-classpath".equals(options[0][0])); + assertTrue("-d".equals(options[1][0])); + assertTrue(null == options[3][0]); + + // get args back, no options set + args = new String[] { "Bar.java" }; + options = LangUtil.copyStrings(OPTIONS); + + result = LangUtil.extractOptions(args, options); + resultString = "" + Arrays.asList(result); + EXP = "[Bar.java]"; + assertTrue(resultString + " != " + EXP, resultString.equals(EXP)); + assertTrue(null == options[0][0]); + assertTrue(null == options[1][0]); + assertTrue(null == options[2][0]); + assertTrue(null == options[3][0]); + } + + public void testOptionVariants() { + String[] NONE = new String[0]; + String[] one = new String[] {"-1"}; + String[] two = new String[] {"-2"}; + String[] three= new String[] {"-3"}; + String[] both = new String[] {"-1", "-2" }; + String[] oneB = new String[] {"-1-"}; + String[] bothB = new String[] {"-1-", "-2-" }; + String[] onetwoB = new String[] {"-1", "-2-" }; + String[] oneBtwo = new String[] {"-1-", "-2" }; + String[] threeB = new String[] {"-1-", "-2-", "-3-"}; + String[] athreeB = new String[] {"a", "-1-", "-2-", "-3-"}; + String[] threeaB = new String[] {"-1-", "a", "-2-", "-3-"}; + + checkOptionVariants(NONE, new String[][] { NONE }); + checkOptionVariants(one, new String[][] { one }); + checkOptionVariants(both, new String[][] { both }); + checkOptionVariants(oneB, new String[][] { NONE, one }); + checkOptionVariants(bothB, new String[][] { NONE, one, new String[] {"-2"}, both }); + checkOptionVariants(onetwoB, new String[][] { one, new String[] {"-1", "-2"}}); + checkOptionVariants(oneBtwo, new String[][] { two, new String[] {"-1", "-2"}}); + checkOptionVariants(threeB, new String[][] + { + NONE, + one, + two, + new String[] {"-1", "-2"}, + three, + new String[] {"-1", "-3"}, + new String[] {"-2", "-3"}, + new String[] {"-1", "-2", "-3"} + }); + checkOptionVariants(athreeB, new String[][] + { + new String[] {"a"}, + new String[] {"a", "-1"}, + new String[] {"a", "-2"}, + new String[] {"a", "-1", "-2"}, + new String[] {"a", "-3"}, + new String[] {"a", "-1", "-3"}, + new String[] {"a", "-2", "-3"}, + new String[] {"a", "-1", "-2", "-3"} + }); + checkOptionVariants(threeaB, new String[][] + { + new String[] {"a"}, + new String[] {"-1", "a"}, + new String[] {"a", "-2"}, + new String[] {"-1", "a", "-2"}, + new String[] {"a", "-3"}, + new String[] {"-1", "a", "-3"}, + new String[] {"a", "-2", "-3"}, + new String[] {"-1", "a", "-2", "-3"} + }); + } + + void checkOptionVariants(String[] options, String[][] expected) { + String[][] result = LangUtil.optionVariants(options); + if (expected.length != result.length) { + assertTrue("exp=" + expected.length + " actual=" + result.length, false); + } + for (int i = 0; i < expected.length; i++) { + assertEquals(""+i, + "" + Arrays.asList(expected[i]), + "" + Arrays.asList(result[i])); + } + } + + /** @see XMLWriterTest#testUnflattenList() */ + public void testCommaSplit() { + checkCommaSplit("", new String[] {""}); + checkCommaSplit("1", new String[] {"1"}); + checkCommaSplit(" 1 2 ", new String[] {"1 2"}); + checkCommaSplit(" 1 , 2 ", new String[] {"1", "2"}); + checkCommaSplit("1,2,3,4", new String[] {"1", "2", "3", "4"}); + } + + void checkCommaSplit(String input, String[] expected) { + List actual = LangUtil.commaSplit(input); + String a = "" + actual; + String e = "" + Arrays.asList(expected); + assertTrue(e + "==" + a, e.equals(a)); + } + + public void testElideEndingLines() { + StringBuffer stackBuffer = LangUtil.stackToString(new RuntimeException(""), true); + LangUtil.elideEndingLines(LangUtil.StringChecker.TEST_PACKAGES, stackBuffer, 100); + String result = stackBuffer.toString(); + if (-1 == result.indexOf("(... ")) { + // brittle - will fail under different top-level drivers + String m = "when running under eclipse or Ant, expecting (... in trace: "; + assertTrue( m + result, false); + } + + stackBuffer = new StringBuffer("java.lang.RuntimeException: unimplemented" + + "\n at org.aspectj.ajdt.internal.core.builder.EclipseUnwovenClassFile.writeWovenBytes(EclipseUnwovenClassFile.java:59)" + + "\n at org.aspectj.bcweaver.bcel.BcelWeaver.dump(BcelWeaver.java:271)" + + "\n at org.aspectj.bcweaver.bcel.BcelWeaver.weave(BcelWeaver.java:233)" + + "\n at org.aspectj.bcweaver.bcel.BcelWeaver.weave(BcelWeaver.java:198)" + + "\n at org.aspectj.ajdt.internal.core.builder.AjBuildManager.weaveAndGenerateClassFiles(AjBuildanager.java:230)" + + "\n at org.aspectj.ajdt.internal.core.builder.AjBuildManager.batchBuild(AjBuildManager.java:50)" + + "\n at org.aspectj.ajdt.ajc.AjdtCommand.runCommand(AjdtCommand.java:42)" + + "\n at org.aspectj.testing.harness.bridge.CompilerRun.run(CompilerRun.java:222)" + + "\n at org.aspectj.testing.run.Runner.runPrivate(Runner.java:363)" + + "\n at org.aspectj.testing.run.Runner.runChild(Runner.java:167)" + + "\n at org.aspectj.testing.run.Runner.runChild(Runner.java:126)" + + "\n at org.aspectj.testing.run.Runner$IteratorWrapper.run(Runner.java:441)" + + "\n at org.aspectj.testing.run.Runner.runPrivate(Runner.java:363)" + + "\n at org.aspectj.testing.run.Runner.runChild(Runner.java:167)" + + "\n at org.aspectj.testing.run.Runner.runChild(Runner.java:126)" + + "\n at org.aspectj.testing.run.Runner$IteratorWrapper.run(Runner.java:441)" + + "\n at org.aspectj.testing.run.Runner.runPrivate(Runner.java:363)" + + "\n at org.aspectj.testing.run.Runner.run(Runner.java:114)" + + "\n at org.aspectj.testing.run.Runner.run(Runner.java:105)" + + "\n at org.aspectj.testing.run.Runner.runIterator(Runner.java:228)" + + "\n at org.aspectj.testing.drivers.Harness.run(Harness.java:254)" + + "\n at org.aspectj.testing.drivers.Harness.runMain(Harness.java:217)" + + "\n at org.aspectj.testing.drivers.Harness.main(Harness.java:99)" + + "\n at org.aspectj.testing.Harness.main(Harness.java:37)" + + "\n clip me"); + + LangUtil.elideEndingLines(LangUtil.StringChecker.TEST_PACKAGES, stackBuffer, 25); + result = stackBuffer.toString(); + assertTrue(result, -1 != result.indexOf("(... ")); + assertTrue(result, -1 == result.indexOf("org.aspectj.testing")); + } +} diff --git a/util/testsrc/org/aspectj/util/UtilTests.java b/util/testsrc/org/aspectj/util/UtilTests.java new file mode 100644 index 000000000..7dc577941 --- /dev/null +++ b/util/testsrc/org/aspectj/util/UtilTests.java @@ -0,0 +1,32 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.util; + +import junit.framework.*; + +public class UtilTests extends TestCase { + + public static Test suite() { + TestSuite suite = new TestSuite(UtilTests.class.getName()); + //$JUnit-BEGIN$ + suite.addTestSuite(FileUtilTest.class); + suite.addTestSuite(LangUtilTest.class); + //$JUnit-END$ + return suite; + } + + public UtilTests(String name) { super(name); } + +} |