/* ******************************************************************* * 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.testing.harness.bridge; import java.io.File; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Iterator; import java.util.ListIterator; import java.util.Stack; import org.aspectj.bridge.AbortException; import org.aspectj.bridge.IMessage; import org.aspectj.bridge.IMessageHandler; import org.aspectj.bridge.MessageUtil; import org.aspectj.util.FileUtil; import org.aspectj.util.LangUtil; /** * Check input and implement defaults. * This handles failure messaging and collecting temp directories * for later cleanup. * The default behavior is to send a fail message to the message * handler. Clients may instead: *
  • Toggle abortOnException to throw AbortException on failure
  • *
  • push their own message handler to redirect messages * (pop to remove if any pushed)
  • *
  • subclass this to reimplement fail(String) *

    * A component given this to do validation should * not change the reporting scheme established by the caller, * so the caller may lock and unlock the error handling policy. * When the policy is locked, this silently ignores attempts * to toggle exceptions, or delete temporary files. * XXX callers cannot prevent others from pushing other error handlers. */ public class Validator { /** stack of handlers */ private final Stack handlers; /** list of File registered for deletion on demand */ private final ArrayList tempFiles; // deleteTempFiles requires ListIterator.remove() /** list of Sandboxes registered for cleanup on demand */ private final ArrayList sandboxes; /** if true, throw AbortException on failure */ boolean abortOnFailure; /** this object prevents any changes to error-handling policy */ private Object locker; public Validator(IMessageHandler handler) { tempFiles = new ArrayList(); sandboxes = new ArrayList(); handlers = new Stack(); pushHandler(handler); } /** * Push IMessageHandler onto stack, * so it will be used until the next push or pop * @param handler not null * */ public void pushHandler(IMessageHandler handler) { LangUtil.throwIaxIfNull(handler, "handler"); handlers.push(handler); } /** @throws IllegalStateException if handler is not on top */ public void popHandler(IMessageHandler handler) { LangUtil.throwIaxIfNull(handler, "handler"); if (handler != handlers.peek()) { throw new IllegalStateException("not current handler"); } handlers.pop(); } /** @return true if this requestor now has locked the error handling policy */ public boolean lock(Object requestor) { if (null == locker) { locker = requestor; } return (locker == requestor); } /** @return true if the error handling policy is now unlocked */ public boolean unlock(Object requestor) { if (requestor == locker) { locker = null; } return (locker == null); } public void setAbortOnFailure(boolean abortOnFailure) { if (null == locker) { if (this.abortOnFailure != abortOnFailure) { this.abortOnFailure = abortOnFailure; } } } /** * May fail with any of the messages *

  • {null check} array
  • *
  • {null check} {message}[#}
  • */ public boolean nullcheck(Object[] ra, String message) { return ((nullcheck((Object) ra, message + " array")) && nullcheck(Arrays.asList(ra), message)); } /** * Like nullcheck(Collection, message), except adding lower and upper bound * @param atLeast fail if list size is smaller than this * @param atMost fail if list size is greater than this */ public boolean nullcheck(Collection list, int atLeast, int atMost, String message) { if (nullcheck(list, message)) { int size = list.size(); if (size < atLeast) { fail(message + ": " + size + "<" + atLeast); } else if (size > atMost) { fail(message + ": " + size + ">" + atMost); } else { return true; } } return false; } /** * May fail with any of the messages *
  • {null check} list
  • *
  • {null check} {message}[#}
  • */ public boolean nullcheck(Collection list, String message) { if (nullcheck((Object) list, message + " list")) { int i = 0; for (Iterator iter = list.iterator(); iter.hasNext();) { if (!nullcheck(iter.next(), message + "[" + i++ + "]")) { return false; } } return true; } return false; } /** * May fail with the message "null {message}" * if o and the def{ault} are null * @return o if not null or default otherwise */ public Object nulldefault(Object o, String message, Object def) { if (null == o) { o = def; } nullcheck(o, message); return o; } /** may fail with the message "null {message}" */ public boolean nullcheck(Object o, String message) { if (null == o) { if (null == message) message = "object"; fail("null " + message); return false; } return true; } /** * Verify that all paths are readable relative to baseDir. * may fail with the message "cannot read {file}" */ public boolean canRead(File baseDir, String[] paths, String message) { if (!canRead(baseDir, "baseDir - " + message) || !nullcheck(paths, "paths - " + message)) { return false; } final String dirPath = baseDir.getPath(); File[] files = FileUtil.getBaseDirFiles(baseDir, paths); for (int j = 0; j < files.length; j++) { if (!canRead(files[j], "{" + dirPath + "} " + files[j])) { return false; } } return true; } /** * Verify that all paths are readable relative to baseDir. * may fail with the message "cannot read {file}" */ public boolean canRead(File[] files, String message) { if (!nullcheck(files, message)) { return false; } for (int j = 0; j < files.length; j++) { if (!canRead(files[j], files[j].getPath())) { return false; } } return true; } /** may fail with the message "cannot read {file}" */ public boolean canRead(File file, String message) { if (nullcheck(file, message)) { if (file.canRead()) { return true; } else { fail("cannot read " + file); } } return false; } /** may fail with the message "cannot write {file}" */ public boolean canWrite(File file, String message) { if (nullcheck(file, message)) { if (file.canRead()) { return true; } else { fail("cannot write " + file); } } return false; } /** may fail with the message "not a directory {file}" */ public boolean canReadDir(File file, String message) { if (canRead(file, message)) { if (file.isDirectory()) { return true; } else { fail("not a directory " + file); } } return false; } /** may fail with the message "not a directory {file}" */ public boolean canWriteDir(File file, String message) { if (canWrite(file, message)) { if (file.isDirectory()) { return true; } else { fail("not a directory " + file); } } return false; } /** * May fail with any of the messages *
  • {null check} dir array
  • *
  • "#: not a File {file}"
  • *
  • {canRead} {message}[#}
  • */ public boolean canReadFiles(Object[] dirs, String message) { return ((nullcheck((Object) dirs, message + " dir array")) && canReadFiles(Arrays.asList(dirs), message)); } /** * May fail with any of the messages *
  • {null check} files
  • *
  • "#: not a File {file}"
  • *
  • {canRead} {message}[#}
  • */ public boolean canReadFiles(Collection dirs, String message) { if (nullcheck((Object) dirs, message + " files")) { int i = 0; for (Iterator iter = dirs.iterator(); iter.hasNext();) { Object o = iter.next(); if (! (o instanceof File)) { fail(i + ": not a file " + o); } if (!canRead((File) o, message + "[" + i++ + "]")) { return false; } } return true; } return false; } /** * May fail with any of the messages *
  • {null check} dir array
  • *
  • "#: not a File {file}"
  • *
  • {canReadDir} {message}[#}
  • */ public boolean canReadDirs(Object[] dirs, String message) { return ((nullcheck((Object) dirs, message + " dir array")) && canReadDirs(Arrays.asList(dirs), message)); } /** * May fail with any of the messages *
  • {null check} dirs
  • *
  • "#: not a File {file}"
  • *
  • {canReadDir} {message}[#}
  • */ public boolean canReadDirs(Collection dirs, String message) { if (nullcheck((Object) dirs, message + " dirs")) { int i = 0; for (Iterator iter = dirs.iterator(); iter.hasNext();) { Object o = iter.next(); if (! (o instanceof File)) { fail(i + ": not a file " + o); } if (!canReadDir((File) o, message + "[" + i++ + "]")) { return false; } } return true; } return false; } /** * May fail with any of the messages *
  • {null check} dir array
  • *
  • "#: not a File {file}"
  • *
  • {canWrite} {message}[#}
  • */ public boolean canWriteFiles(Object[] dirs, String message) { return ((nullcheck((Object) dirs, message + " dir array")) && canWriteFiles(Arrays.asList(dirs), message)); } /** * May fail with any of the messages *
  • {null check} files
  • *
  • "#: not a File {file}"
  • *
  • {canWrite} {message}[#}
  • */ public boolean canWriteFiles(Collection dirs, String message) { if (nullcheck((Object) dirs, message + " files")) { int i = 0; for (Iterator iter = dirs.iterator(); iter.hasNext();) { Object o = iter.next(); if (! (o instanceof File)) { fail(i + ": not a file " + o); } if (!canWrite((File) o, message + "[" + i++ + "]")) { return false; } } return true; } return false; } /** * May fail with any of the messages *
  • {null check} dir array
  • *
  • "#: not a File {file}"
  • *
  • {canWriteDir} {message}[#}
  • */ public boolean canWriteDirs(Object[] dirs, String message) { return ((nullcheck((Object) dirs, message + " dir array")) && canWriteDirs(Arrays.asList(dirs), message)); } /** * May fail with any of the messages *
  • {null check} dirs
  • *
  • "#: not a File {file}"
  • *
  • {canWriteDir} {message}[#}
  • */ public boolean canWriteDirs(Collection dirs, String message) { if (nullcheck((Object) dirs, message + " dirs")) { int i = 0; for (Iterator iter = dirs.iterator(); iter.hasNext();) { Object o = iter.next(); if (! (o instanceof File)) { fail(i + ": not a file " + o); } if (!canWriteDir((File) o, message + "[" + i++ + "]")) { return false; } } return true; } return false; } /** * Send an info message to any underlying handler * @param message ignored if null */ public void info(String message) { if (null != message) { IMessageHandler handler = getHandler(); MessageUtil.info(handler, message); } } /** Fail via message or AbortException */ public void fail(String message) { fail(message, (Throwable) null); } /** * Fail via message or AbortException. * All failure messages go through here, * so subclasses may override to control * failure-handling. */ public void fail(String message, Throwable thrown) { if ((null == message) && (null == thrown)) { message = ""; } IMessage m = MessageUtil.fail(message, thrown); if (abortOnFailure) { throw new AbortException(m); } else { IMessageHandler handler = getHandler(); handler.handleMessage(m); } } /** * Register a file temporary, i.e., to be * deleted on completion. The file need not * exist (yet or ever) and may be a duplicate * of existing files registered. */ public void registerTempFile(File file) { if (null != file) { tempFiles.add(file); } } /** * Get a writable {possibly-empty} directory. * If the input dir is null, then try to create a temporary * directory using name. * If the input dir is not null, this tries to * create it if it does not exist. * Then if name is not null, * it tries to get a temporary directory * under this using name. * If name is null, "Validator" is used. * If deleteContents is true, this will try to delete * any existing contents. If this is unable to delete * the contents of the input directory, this may return * a new, empty temporary directory. * If register is true, then any directory returned is * saved for later deletion using deleteTempDirs(), * including the input directory. * When this is unable to create a result, if failMessage * is not null then this will fail; otherwise it returns false; */ public File getWritableDir(File dir, String name, boolean deleteContents, boolean register, String failMessage) { // check dir if (null == dir) { if (null == name) { name = "Validator"; } dir = FileUtil.getTempDir(name); } else { if (!dir.exists()) { dir.mkdirs(); } } // fail if necessary if ((null == dir) || (!dir.exists())) { if (null != failMessage) { fail(failMessage + ": unable to get parent " + dir); } } else { FileUtil.makeNewChildDir(dir, name); if (deleteContents) { FileUtil.deleteContents(dir); } if (register) { tempFiles.add(dir); } } return dir; } /** * Delete any temp sandboxes, files or directories saved, * optionally reporting failures in the normal way. * This will be ignored unless the failure policy * is unlocked. */ public void deleteTempFiles(boolean reportFailures) { if (null == locker) { for (ListIterator iter = tempFiles.listIterator(); iter.hasNext();) { if (deleteFile((File) iter.next(), reportFailures)) { iter.remove(); } } for (ListIterator iter = sandboxes.listIterator(); iter.hasNext();) { Sandbox sandbox = (Sandbox) iter.next(); // XXX assumes all dirs are in sandboxDir if (deleteFile(sandbox.sandboxDir, reportFailures)) { iter.remove(); } } } } /** * Manage temp files and directories of registered sandboxes. * Note that a sandbox may register before it is initialized, * so this must do nothing other than save the reference. * @param sandbox the uninitialized Sandbox to track */ public void registerSandbox(Sandbox sandbox) { sandboxes.add(sandbox); } private boolean deleteFile(File file, boolean reportFailures) { if (null == file) { if (reportFailures) { fail("unable to delete null file"); } return true; // null file - skip } FileUtil.deleteContents(file); if (file.exists()) { file.delete(); } if (!file.exists()) { return true; } else if (reportFailures) { fail("unable to delete " + file); } return false; } /** @throws IllegalStateException if handler is null */ private IMessageHandler getHandler() { IMessageHandler handler = (IMessageHandler) handlers.peek(); if (null == handler) { throw new IllegalStateException("no handler"); } return handler; } } // class Validator