/*
 * JBoss, Home of Professional Open Source
 * Copyright 2015, Red Hat, Inc., and individual contributors as indicated
 * by the @authors tag.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.jboss.util.propertyeditor;

import java.net.InetAddress;
import java.net.UnknownHostException;

import org.jboss.util.NestedRuntimeException;
import org.jboss.util.StringPropertyReplacer;

/**
 * A property editor for {@link java.net.InetAddress}.
 *
 * @version <tt>$Revision$</tt>
 * @author  Adrian Brock
 */
public class InetAddressEditor extends TextPropertyEditorSupport
{
   /**
    * Returns a InetAddress for the input object converted to a string.
    *
    * @return an InetAddress
    *
    * @throws NestedRuntimeException   An UnknownHostException occured.
    */
   public Object getValue()
   {
      try
      {
         String text = getAsText();
         if (text == null)
         {
            return null;
         }
         if (text.startsWith("/"))
         {
            // seems like localhost sometimes will look like:
            // /127.0.0.1 and the getByNames barfs on the slash - JGH
            text = text.substring(1);
         }
         return InetAddress.getByName(StringPropertyReplacer.replaceProperties(text));
      }
      catch (UnknownHostException e)
      {
         throw new NestedRuntimeException(e);
      }
   }
}


/*
 * JBoss, Home of Professional Open Source
 * Copyright 2015, Red Hat, Inc., and individual contributors as indicated
 * by the @authors tag.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.jboss.util.file;

import java.io.File;
import java.io.FileFilter;

/**
 * A suffix based file filter.
 *
 * @version <tt>$Revision$</tt>
 * @author  Jason Dillon
 */
public class FileSuffixFilter
   implements FileFilter
{
   /** A list of suffixes which files must have to be accepted. */
   protected final String suffixes[];

   /** Flag to signal that we want to ignore the case. */
   protected final boolean ignoreCase;

   /**
    * Construct a <tt>FileSuffixFilter</tt>.
    *
    * @param suffixes   A list of suffixes which files mut have to be accepted.
    * @param ignoreCase <tt>True</tt> if the filter should be case-insensitive.
    */
   public FileSuffixFilter(final String suffixes[],
                           final boolean ignoreCase)
   {
      this.ignoreCase = ignoreCase;
      if (ignoreCase) {
         this.suffixes = new String[suffixes.length];
         for (int i=0; i<suffixes.length; i++) {
            this.suffixes[i] = suffixes[i].toLowerCase();
         }
      }
      else {
         this.suffixes = suffixes;
      }
   }

   /**
    * Construct a <tt>FileSuffixFilter</tt>.
    *
    * @param suffixes   A list of suffixes which files mut have to be accepted.
    */
   public FileSuffixFilter(final String suffixes[])
   {
      this(suffixes, false);
   }

   /**
    * Construct a <tt>FileSuffixFilter</tt>.
    *
    * @param suffix     The suffix which files must have to be accepted.
    * @param ignoreCase <tt>True</tt> if the filter should be case-insensitive.
    */
   public FileSuffixFilter(final String suffix,
                           final boolean ignoreCase)
   {
      this(new String[] { suffix }, ignoreCase);
   }

   /**
    * Construct a case sensitive <tt>FileSuffixFilter</tt>.
    *
    * @param suffix  The suffix which files must have to be accepted.
    */
   public FileSuffixFilter(final String suffix) {
      this(suffix, false);
   }

   /**
    * Check if a file is acceptible.
    *
    * @param file    The file to check.
    * @return        <tt>true</tt> if the file is acceptable.
    */
   public boolean accept(final File file) {
      boolean success = false;

      for (int i=0; i<suffixes.length && !success; i++) {
         if (ignoreCase)
            success = file.getName().toLowerCase().endsWith(suffixes[i]);
         else
            success = file.getName().endsWith(suffixes[i]);
      }

      return success;
   }
}


/*
 * JBoss, Home of Professional Open Source
 * Copyright 2015, Red Hat, Inc., and individual contributors as indicated
 * by the @authors tag.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.jboss.util.collection;

import java.util.List;
import java.util.LinkedList;
import java.util.AbstractList;

import java.lang.ref.ReferenceQueue;

import org.jboss.util.SoftObject;
import org.jboss.util.Objects;

/**
 * A wrapper around a List which translates added objects
 * into {@link SoftObject} references, allowing the VM to garbage collect
 * objects in the collection when memory is low.
 *
 * @version <tt>$Revision$</tt>
 * @author  Jason Dillon
 */
@SuppressWarnings("unchecked")
public class CachedList
   extends AbstractList
{
   /** Reference queue. */
   protected final ReferenceQueue queue = new ReferenceQueue();

   /** Wrapped list. */
   protected final List list;

   /**
    * Construct a <tt>CachedList</tt>.
    *
    * @param list    List to wrap.
    */
   public CachedList(final List list) {
      this.list = list;
   }

   /**
    * Construct a <tt>CachedList</tt> using a <tt>LinkedList</tt> for
    * storage.
    */
   public CachedList() {
      this(new LinkedList());
   }

   /**
    * Dereference the object at the given index.
    */
   private Object getObject(final int index) {
      Object obj = list.get(index);

      return Objects.deref(obj);
   }

   /**
    * Returns the element at the specified position in this list.
    *
    * @param index   Index of element to return.
    * @return        The element at the specified position.
    */
   public Object get(final int index) {
      maintain();
      return getObject(index);
   }

   /**
    * Return the size of the list.
    *
    * @return  The number of elements in the list.
    */
   public int size() {
      maintain();
      return list.size();
   }

   /**
    * Replaces the element at the specified position in this list with the
    * specified element.
    *
    * @param index   Index of element to replace.
    * @param obj     Element to be stored at the specified postion.
    * @return        The previous element at the given index.
    */
   public Object set(final int index, final Object obj) {
      maintain();

      SoftObject soft = SoftObject.create(obj, queue);
      soft = (SoftObject)list.set(index, soft);

      return Objects.deref(soft);
   }

   /**
    * Inserts the specified element at the specified position in this list
    * (optional operation). Shifts the element currently at that position
    * (if any) and any subsequent elements to the right (adds one to their
    * indices).
    *
    * @param index   Index at which the specified element is to be inserted.
    * @param obj     Element to be inserted.
    */
   public void add(final int index, final Object obj) {
      maintain();

      SoftObject soft = SoftObject.create(obj, queue);
      list.add(index, soft);
   }

   /**
    * Removes the element at the specified position in this list (optional
    * operation). Shifts any subsequent elements to the left (subtracts one
    * from their indices). Returns the element that was removed from the list.
    *
    * @param index   The index of the element to remove.
    * @return        The element previously at the specified position.
    */
   public Object remove(final int index) {
      maintain();

      Object obj = list.remove(index);
      return Objects.deref(obj);
   }

   /**
    * Maintains the collection by removing garbage collected objects.
    */
   private void maintain() {
      SoftObject obj;
      int count = 0;

      while ((obj = (SoftObject)queue.poll()) != null) {
         count++;
         list.remove(obj);
      }

      if (count != 0) {
         // some temporary debugging fluff
         System.err.println("vm reclaimed " + count + " objects");
      }
   }
}


/*
 * JBoss, Home of Professional Open Source
 * Copyright 2015, Red Hat, Inc., and individual contributors as indicated
 * by the @authors tag.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// CatalogEntry.java - Represents Catalog entries

package org.jboss.util.xml.catalog;

import java.util.Hashtable;
import java.util.Vector;

/**
 * Represents a Catalog entry.
 *
 *
Instances of this class represent individual entries
 * in a Catalog.

 *
 *
Each catalog entry has a unique name and is associated with
 * an arbitrary number of arguments (all strings). For example, the
 * TR9401 catalog entry "PUBLIC" has two arguments, a public identifier
 * and a system identifier. Each entry has a unique numeric type,
 * assigned automatically when the entry type is created.

 *
 *
The number and type of catalog entries is maintained
 * statically. Catalog classes, or their subclasses, can add
 * new entry types, but all Catalog objects share the same global pool
 * of types.

 *
 *
Initially there are no valid entries.

 *
 * @see Catalog
 *
 * @author Norman Walsh
 * Norman.Walsh@Sun.COM
 *
 * @version 1.0
 */
@SuppressWarnings("unchecked")
public class CatalogEntry {
  /** The nextEntry is the ordinal number of the next entry type. */
  protected static int nextEntry = 0;

  /**
   * The entryTypes vector maps catalog entry names
   * (e.g., 'BASE' or 'SYSTEM') to their type (1, 2, etc.).
   * Names are case sensitive.
   */
  protected static Hashtable entryTypes = new Hashtable();

  /** The entryTypes vector maps catalog entry types to the
      number of arguments they're required to have. */
  protected static Vector entryArgs = new Vector();

  /**
   * Adds a new catalog entry type.
   *
   * @param name The name of the catalog entry type. This must be
   * unique among all types and is case-sensitive. (Adding a duplicate
   * name effectively replaces the old type with the new type.)
   * @param numArgs The number of arguments that this entry type
   * is required to have. There is no provision for variable numbers
   * of arguments.
   * @return The type for the new entry.
   */
  public static int addEntryType(String name, int numArgs) {
    entryTypes.put(name, new Integer(nextEntry));
    entryArgs.add(nextEntry, new Integer(numArgs));
    nextEntry++;

    return nextEntry-1;
  }

  /**
   * Lookup an entry type
   *
   * @param name The name of the catalog entry type.
   * @return The type of the catalog entry with the specified name.
   * @throws CatalogException for any error
   */
  public static int getEntryType(String name)
    throws CatalogException {
    if (!entryTypes.containsKey(name)) {
      throw new CatalogException(CatalogException.INVALID_ENTRY_TYPE);
    }

    Integer iType = (Integer) entryTypes.get(name);

    if (iType == null) {
      throw new CatalogException(CatalogException.INVALID_ENTRY_TYPE);
    }

    return iType.intValue();
  }

  /**
   * Find out how many arguments an entry is required to have.
   *
   * @param name The name of the catalog entry type.
   * @return The number of arguments that entry type is required to have.
   * @throws CatalogException for any error
   */
  public static int getEntryArgCount(String name)
    throws CatalogException {
    return getEntryArgCount(getEntryType(name));
  }

  /**
   * Find out how many arguments an entry is required to have.
   *
   * @param type A valid catalog entry type.
   * @return The number of arguments that entry type is required to have.
   * @throws CatalogException for any error
   */
  public static int getEntryArgCount(int type)
    throws CatalogException {
    try {
      Integer iArgs = (Integer) entryArgs.get(type);
      return iArgs.intValue();
    } catch (ArrayIndexOutOfBoundsException e) {
      throw new CatalogException(CatalogException.INVALID_ENTRY_TYPE);
    }
  }

  /** The entry type of this entry */
  protected int entryType = 0;

  /** The arguments associated with this entry */
  protected Vector args = null;

  /**
   * Null constructor; something for subclasses to call.
   */
  public CatalogEntry() {}

  /**
   * Construct a catalog entry of the specified type.
   *
   * @param name The name of the entry type
   * @param args A String Vector of arguments
   * @throws CatalogException for any error
   */
  public CatalogEntry(String name, Vector args)
    throws CatalogException {
    Integer iType = (Integer) entryTypes.get(name);

    if (iType == null) {
      throw new CatalogException(CatalogException.INVALID_ENTRY_TYPE);
    }

    int type = iType.intValue();

    try {
      Integer iArgs = (Integer) entryArgs.get(type);
      if (iArgs.intValue() != args.size()) {
	throw new CatalogException(CatalogException.INVALID_ENTRY);
      }
    } catch (ArrayIndexOutOfBoundsException e) {
      throw new CatalogException(CatalogException.INVALID_ENTRY_TYPE);
    }

    entryType = type;
    this.args = args;
  }

  /**
   * Construct a catalog entry of the specified type.
   *
   * @param type
   * @param args A String Vector of arguments
   * @throws CatalogException for any error
   */
  public CatalogEntry(int type, Vector args)
    throws CatalogException {
    try {
      Integer iArgs = (Integer) entryArgs.get(type);
      if (iArgs.intValue() != args.size()) {
	throw new CatalogException(CatalogException.INVALID_ENTRY);
      }
    } catch (ArrayIndexOutOfBoundsException e) {
      throw new CatalogException(CatalogException.INVALID_ENTRY_TYPE);
    }

    entryType = type;
    this.args = args;
  }

  /**
   * Get the entry type.
   *
   * @return The entry type of the CatalogEntry
   */
  public int getEntryType() {
    return entryType;
  }

  /**
   * Get an entry argument.
   *
   * @param argNum The argument number (arguments are numbered from 0).
   * @return The specified argument or null if an invalid argNum is
   * provided.
   */
  public String getEntryArg(int argNum) {
    try {
      String arg = (String) args.get(argNum);
      return arg;
    } catch (ArrayIndexOutOfBoundsException e) {
      return null;
    }
  }

  /**
   * Set an entry argument.
   *
   *
Catalogs sometimes need to adjust the catlog entry parameters,
   * for example to make a relative URI absolute with respect to the
   * current base URI. But in general, this function should only be
   * called shortly after object creation to do some sort of cleanup.
   * Catalog entries should not mutate over time.

   *
   * @param argNum The argument number (arguments are numbered from 0).
 * @param newspec
   * @throws ArrayIndexOutOfBoundsException if an invalid argument
   * number is provided.
   */
  public void setEntryArg(int argNum, String newspec)
    throws ArrayIndexOutOfBoundsException {
    args.set(argNum, newspec);
  }
}


/*
 * JBoss, Home of Professional Open Source
 * Copyright 2015, Red Hat, Inc., and individual contributors as indicated
 * by the @authors tag.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.jboss.util;

import java.util.Comparator;

/**
 * Data structure that mantains data in a ordered binary tree; each node is
 * greater (smaller) or equal than its 2 sub-nodes, for all the hierarchy.

 * Elements of this data structure should either implement Comparable, or a
 * Comparator should be given as argument to the constructor.
 *
 * @author Simone Bordet
 * @version $Revision$
 */
@SuppressWarnings("unchecked")
public class Heap
{
   private Comparator m_comparator;
   private int m_count;
   private Object[] m_nodes;

   /**
    * Creates a new Heap whose elements inserted implement the
    * {@link Comparable} interface.
    */
   public Heap()
   {
      this(null);
   }

   /**
    * Creates a new Heap whose elements are compared using the given
    * {@link Comparator}.
    * @param comparator
    */
   public Heap(Comparator comparator)
   {
      m_comparator = comparator;
      clear();
   }

   /**
    * Inserts the given element in this heap.
    * @param obj
    *
    * @see #extract
    */
   public void insert(Object obj)
   {
      int length = m_nodes.length;
      // Expand if necessary
      if (m_count == length)
      {
         Object[] newNodes = new Object[length + length];
         System.arraycopy(m_nodes, 0, newNodes, 0, length);
         m_nodes = newNodes;
      }
      // Be cur_slot the first unused slot index; be par_slot its parent index.
      // Start from cur_slot and walk up the tree comparing the object to
      // insert with the object at par_slot; if it's smaller move down the object at par_slot,
      // otherwise cur_slot is the index where insert the object. If not done,
      // shift up the tree so that now cur_slot is the old par_slot and
      // par_slot is the parent index of the new cur_slot (so the grand-parent
      // index of the old cur_slot) and compare again.
      int k = m_count;
      while (k > 0)
      {
         int par = parent(k);
         if (compare(obj, m_nodes[par]) < 0)
         {
            m_nodes[k] = m_nodes[par];
            k = par;
         }
         else break;
      }
      m_nodes[k] = obj;
      ++m_count;
   }

   /**
    * Removes and returns the least element of this heap.
    * @return the extracted object
    *
    * @see #insert
    * @see #peek
    */
   public Object extract()
   {
      if (m_count < 1) {return null;}
      else
      {
         int length = m_nodes.length >> 1;
         // Shrink if necessary
         if (length > 5 && m_count < (length >> 1))
         {
            Object[] newNodes = new Object[length];
            System.arraycopy(m_nodes, 0, newNodes, 0, length);
            m_nodes = newNodes;
         }
         //
         int k = 0;
         Object ret = m_nodes[k];
         --m_count;
         Object last = m_nodes[m_count];
         for (;;)
         {
            int l = left(k);
            if (l >= m_count) {break;}
            else
            {
               int r = right(k);
               int child = (r >= m_count || compare(m_nodes[l], m_nodes[r]) < 0) ? l : r;
               if (compare(last, m_nodes[child]) > 0)
               {
                  m_nodes[k] = m_nodes[child];
                  k = child;
               }
               else {break;}
            }
         }
         m_nodes[k] = last;
         m_nodes[m_count] = null;
         return ret;
      }
   }

   /**
    * @return without removing it, the least element of this heap.
    *
    * @see #extract
    */
   public Object peek()
   {
      if (m_count < 1) {return null;}
      else {return m_nodes[0];}
   }

   /**
    * Empties this heap
    */
   public void clear()
   {
      m_count = 0;
      m_nodes = new Object[10];
   }

   protected int compare(Object o1, Object o2)
   {
      if (m_comparator != null)
      {
         return m_comparator.compare(o1, o2);
      }
      else
      {
         if (o1 == null)
         {
            if (o2 == null) {return 0;}
            else {return -((Comparable)o2).compareTo(o1);}
         }
         else {return ((Comparable)o1).compareTo(o2);}
      }
   }

   /**
    * @return the parent index of index.
    * @param index
    */
   protected int parent(int index)
   {
      return (index - 1) >> 1;
   }

   /**
    * @param index
    * @return the left child index of index.
    */
   protected int left(int index)
   {
      return index + index + 1;
   }

   /**
    * @param index
    * @return the right child index of index.
    */
   protected int right(int index)
   {
      return index + index + 2;
   }
}


/*
 * JBoss, Home of Professional Open Source
 * Copyright 2015, Red Hat, Inc., and individual contributors as indicated
 * by the @authors tag.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.jboss.util.propertyeditor;

import java.beans.PropertyEditorSupport;

/** A property editor for byte[].
 *
 * @author Scott.Stark@jboss.org
 * @version $Revision$
 */
public class ByteArrayEditor extends PropertyEditorSupport
{
   /** Map the argument text into and Byte using Byte.decode.
    */
   public void setAsText(final String text)
   {
      if (PropertyEditors.isNull(text, false, false))
      {
         setValue(null);
         return;
      }
      Object newValue = text.getBytes();
      setValue(newValue);
   }
}


/*
 * JBoss, Home of Professional Open Source
 * Copyright 2015, Red Hat, Inc., and individual contributors as indicated
 * by the @authors tag.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.jboss.util.loading;

import java.security.AccessController;
import java.security.PrivilegedAction;

/**
 * A helper for context classloading switching.

 *
 * When a security manager is installed, the
 * constructor checks for the runtime permissions
 * "getClassLoader" and "setContextClassLoader".
 * This allows the methods of this class to be used later without
 * having to run in privileged blocks.
 *
 * There are optimized methods to perform the operations within
 * a switch context. This avoids retrieving the current thread
 * on every operation.
 *
 * @version <tt>$Revision$</tt>
 * @author  Adrian Brock
 */
@SuppressWarnings("unchecked")
public class ContextClassLoaderSwitcher
   extends ContextClassLoader
{
   /**
    * Set the context classloader permission
    */
   public static final RuntimePermission SETCONTEXTCLASSLOADER = new RuntimePermission("setContextClassLoader");

   /**
    * Instantiate a new context class loader switcher
    */
   @SuppressWarnings("hiding")
   public static final NewInstance INSTANTIATOR = new NewInstance();

   /**
    * Constructor.
    * @throws SecurityException when not authroized to get/set the context classloader
    */
   private ContextClassLoaderSwitcher()
   {
      super();
      SecurityManager manager = System.getSecurityManager();
      if (manager != null)
         manager.checkPermission(SETCONTEXTCLASSLOADER);
   }

   /**
    * Set the context classloader
    * @param cl
    */
   public void setContextClassLoader(final ClassLoader cl)
   {
      setContextClassLoader(Thread.currentThread(), cl);
   }

   /**
    * Set the context classloader for the given thread
    *
    * @param thread the thread
    * @param cl the new context classloader
    */
   public void setContextClassLoader(final Thread thread, final ClassLoader cl)
   {
      AccessController.doPrivileged(new PrivilegedAction()
      {
         public Object run()
         {
            thread.setContextClassLoader(cl);
            return null;
         }
      });
   }

   /**
    * Retrieve a switch context
    *
    * @return the switch context
    */
   public SwitchContext getSwitchContext()
   {
      return new SwitchContext();
   }

   /**
    * Retrieve a switch context and set the new context classloader
    *
    * @param cl the new classloader
    * @return the switch context
    */
   public SwitchContext getSwitchContext(final ClassLoader cl)
   {
      return new SwitchContext(cl);
   }

   /**
    * Retrieve a switch context for the classloader of a given class
    *
    * @deprecated using a class to determine the classloader is a
    *             bad idea, it has the same problems as Class.forName()
    * @param clazz the class whose classloader should be set
    *        as the context classloader
    * @return the switch context
    */
   public SwitchContext getSwitchContext(final Class clazz)
   {
      return new SwitchContext(clazz.getClassLoader());
   }

   /**
    * A helper class to remember the original classloader and
    * avoid continually retrieveing the current thread.
    */
   public class SwitchContext
   {
      /**
       * The original classloader
       */
      private ClassLoader origCL;

      /**
       * The current classloader
       */
      private ClassLoader currentCL;

      /**
       * The current thread
       */
      private Thread currentThread;

      private SwitchContext()
      {
         currentThread = Thread.currentThread();
         origCL = getContextClassLoader(currentThread);
         currentCL = origCL;
      }

      private SwitchContext(ClassLoader cl)
      {
         this();
         setClassLoader(cl);
      }

      /**
       * @return the current thread
       */
      public Thread getThread()
      {
         return currentThread;
      }

      /**
       * @return the original classloader
       */
      public ClassLoader getOriginalClassLoader()
      {
         return origCL;
      }

      /**
       * @return the current classloader
       * (as set through this class).
       */
      public ClassLoader getCurrentClassLoader()
      {
         return currentCL;
      }

      /**
       * Change the context classloader

       *
       * The operation is ignored if the classloader is null
       * or has not changed
       *
       * @param cl the new classloader
       */
      public void setClassLoader(ClassLoader cl)
      {
         if (cl != null && cl != currentCL)
         {
            setContextClassLoader(currentThread, cl);
            currentCL = cl;
         }
      }

      /**
       * Reset back to the original classloader,
       * only when it has changed.
       */
      public void reset()
      {
         if (currentCL != null && currentCL != origCL)
            setContextClassLoader(currentThread, origCL);
      }

      /**
       * Force a reset back to the original classloader,
       * useful when somebody else might have changed
       * the thread context classloader so we cannot optimize
       */
      public void forceReset()
      {
         setContextClassLoader(currentThread, origCL);
      }
   }

   private static class NewInstance
      implements PrivilegedAction
   {
      public Object run()
      {
         return new ContextClassLoaderSwitcher();
      }
   }
}


/*
 * JBoss, Home of Professional Open Source
 * Copyright 2015, Red Hat, Inc., and individual contributors as indicated
 * by the @authors tag.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.jboss.util.timeout;

/**
 * TimeoutPriorityQueueImpl.
 *
 *  This is a balanced binary tree. If nonempty, the root is at index 1,
 *  and all nodes are at indices 1..size. Nodes with index greater than
 *  size are null. Index 0 is never used.
 *  Children of the node at index j are at j*2
 *  and j*2+1. The children of a node always fire the timeout
 *  no earlier than the node.
 *
 *
 *  Or, more formally:
 *
 *  Only indices 1..size of this array are used.
 *  All other indices contain the null reference.
 *  This array represent a balanced binary tree.
 *
 *  If size is 0 the tree is empty, otherwise
 *  the root of the tree is at index 1.
 *
 *  Given an arbitrary node at index n that is not the root
 *  node, the parent node of n is at index n/2.
 *
 *  Given an arbitrary node at index n; if
 *  2*n <= size the node at n has its left child
 *  at index 2*n, otherwise the node at n has
 *  no left child.
 *
 *  Given an arbitrary node at index n; if
 *  2*n+1 <= size the node at n has its right child
 *  at index 2*n+1, otherwise the node at n has
 *  no right child.
 *
 *  The priority function is called T. Given a node n,
 *  T(n) denotes the absolute time (in milliseconds since
 *  the epoch) that the timeout for node n should happen.
 *  Smaller values of T means higher priority.
 *
 *  The tree satisfies the following invariant:
 *
 *  For any node n in the tree:
 *  If node n has a left child l,
 *  T(n) <= T(l).
 *  If node n has a right child r,
 *  T(n) <= T(r).
 *
 *
 *
 *  The invariant may be temporarily broken while executing synchronized
 *  on this instance, but is always reestablished before
 *  leaving the synchronized code.
 *
 *  The node at index 1 is always the first node to timeout,
 *  as can be deduced from the invariant.
 *
 *  For the following algorithm pseudocode, the operation
 *  swap(n,m) denotes the exchange of the nodes at indices
 *  n and m in the tree.
 *
 *  Insertion of a new node happend as follows:
 *

 *    IF size = q.length THEN
 *      "expand q array to be larger";
 *    ENDIF
 *    size <- size + 1;
 *    q[size] <- "new node";
 *    n <- size;
 *    WHILE n > 1 AND T(n/2) > T(n) DO
 *      swap(n/2, n);
 *      n <- n/2;
 *    ENDWHILE
 *

 *  Proof that this insertion algorithm respects the invariant is left to
 *  the interested reader.
 *
 *  The removal algorithm is a bit more complicated. To remove the node
 *  at index n:
 *

 *    swap(n, size);
 *    size <- size - 1;
 *    IF n > 1 AND T(n/2) > T(n) THEN
 *      WHILE n > 1 AND T(n/2) > T(n) DO
 *        swap(n/2, n);
 *        n <- n/2;
 *      ENDWHILE
 *    ELSE
 *      WHILE 2*n <= size DO
 *        IF 2*n+1 <= size THEN
 *          // Both children present
 *          IF T(2*n) <= T(2*n+1) THEN
 *            IF T(n) <= T(2*n) THEN
 *              EXIT;
 *            ENDIF
 *            swap(n, 2*n);
 *            n <- 2*n;
 *          ELSE
 *            IF T(n) <= T(2*n+1) THEN
 *              EXIT;
 *            ENDIF
 *            swap(n, 2*n+1);
 *            n <- 2*n+1;
 *          ENDIF
 *        ELSE
 *          // Only left child, right child not present.
 *          IF T(n) <= T(2*n) THEN
 *            EXIT;
 *          ENDIF
 *          swap(n, 2*n);
 *          n <- 2*n;
 *        ENDIF
 *      ENDWHILE
 *    ENDIF
 *

 *  Proof that this removal algorithm respects the invariant is left to
 *  the interested reader. Really, I am not going to prove it here.
 *
 *  If you are interested, you can find this data structure and its
 *  associated operations in most textbooks on algorithmics.
 *
 * @author Ole Husgaard
 * @author Dimitris Andreadis
 * @author Elias Ross
 * @author Adrian Brock
 * @version $Revision$
 */
public class TimeoutPriorityQueueImpl implements TimeoutPriorityQueue
{
   //  Code commented out with the mark "INV:" are runtime checks
   //  of invariants that are not needed for a production system.
   //  For problem solving, you can remove these comments.
   //  Multithreading notes:
   //
   //  While a TimeoutImpl is enqueued, its index field contains the index
   //  of the instance in the queue; that is, for 1 <= n <= size,
   //  q[n].index = n.
   //  Modifications of an enqueued TimeoutImpl instance may only happen
   //  in code synchronized on the TimeoutFactory instance that has it
   //  enqueued.
   //  Modifications on the priority queue may only happen while running in
   //  code synchronized on the TimeoutFactory instance that holds the queue.
   //  When a TimeoutImpl instance is no longer enqueued, its index field
   //  changes to one of the negative constants declared in the TimeoutImpl
   //  class.
   //
   //  Cancellation may race with the timeout.
   //  To avoid problems with this, the TimeoutImpl index field is set to
   //  TimeoutImpl.TIMEOUT when the TimeoutImpl is taken out of the queue.
   //  Finally the index field is set to TimeoutImpl.DONE, and
   //  the TimeoutImpl instance is discarded.

   /** The lock object */
   private Object lock = new Object();

   /** The size of the timeout queue. */
   private int size;

   /** The timeouts */
   private TimeoutExtImpl[] queue;

   /**
    * Create a new TimeoutPriorityQueueImpl.
    */
   public TimeoutPriorityQueueImpl()
   {
      queue = new TimeoutExtImpl[16];
      size = 0;
   }

   public TimeoutExt offer(long time, TimeoutTarget target)
   {
      if (queue == null)
         throw new IllegalStateException("TimeoutPriorityQueue has been cancelled");
      if (time < 0)
         throw new IllegalArgumentException("Negative time");
      if (target == null)
         throw new IllegalArgumentException("Null timeout target");

      synchronized (lock)
      {
         // INV: checkTree();
         // INV: assertExpr(size < queue.length);
         if (++size == queue.length)
         {
            TimeoutExtImpl[] newQ = new TimeoutExtImpl[2 * queue.length];
            System.arraycopy(queue, 0, newQ, 0, queue.length);
            queue = newQ;
         }
         // INV: assertExpr(size < queue.length);
         // INV: assertExpr(queue[size] == null);
         TimeoutExtImpl timeout;
         timeout = queue[size] = new TimeoutExtImpl();
         timeout.index = size;
         timeout.time = time;
         timeout.target = target;
         normalizeUp(size);
         if (timeout.index == 1)
            lock.notify();
         // INV: checkTree();
         return timeout;
      }
   }

   public TimeoutExt take()
   {
      return poll(-1);
   }

   public TimeoutExt poll()
   {
      return poll(1);
   }

   public TimeoutExt poll(long wait)
   {
      long endWait = -1;
      if (wait > 0)
         endWait = System.currentTimeMillis() + wait;
      // Look for work
      synchronized (lock)
      {
         while (queue != null && (wait >= 0 || endWait == -1))
         {
            if (size == 0)
            {
               try
               {
                  if (endWait == -1)
                     lock.wait();
                  else
                     lock.wait(wait);
               }
               catch (InterruptedException ex)
               {
               }
            }
            else
            {
               long now = System.currentTimeMillis();
               if (queue[1].time > now)
               {
                  long waitForFirst = queue[1].time - now;
                  if (endWait != -1 && waitForFirst > wait)
                     waitForFirst = wait;
                  try
                  {
                     lock.wait(waitForFirst);
                  }
                  catch (InterruptedException ex)
                  {
                  }
               }
               if (size > 0 && queue != null && queue[1].time <= System.currentTimeMillis())
               {
                  TimeoutExtImpl result = removeNode(1);
                  result.index = TimeoutExtImpl.TIMEOUT;
                  return result;
               }
            }
            if (endWait != -1)
               wait = endWait - System.currentTimeMillis();
         }
      }
      return null;
   }

   public TimeoutExt peek()
   {
      synchronized (lock)
      {
         if (size > 0)
            return queue[1];
         else
            return null;
      }
   }

   public boolean remove(TimeoutExt timeout)
   {
      TimeoutExtImpl timeoutImpl = (TimeoutExtImpl) timeout;
      synchronized (lock)
      {
         if (timeoutImpl.index > 0)
         {
            // Active timeout, remove it.
            // INV: assertExpr(queue[timeoutImpl.index] == timeout);
            // INV: checkTree();
            removeNode(timeoutImpl.index);
            // INV: checkTree();
            timeoutImpl.index = TimeoutExtImpl.DONE;

            // execution cancelled
            return true;
         }
         else
         {
            // has already been executed (DONE) or
            // is currently executing (TIMEOUT)
            return false;
         }
      }
   }

   public void clear()
   {
      synchronized (lock)
      {
         if (queue == null)
            return;

         // cleanup queue
         for (int i = 1; i <= size; ++i)
            queue[i] = cleanupTimeoutExtImpl(queue[i]);
      }
   }

   public void cancel()
   {
      synchronized (lock)
      {
         if (queue == null)
            return;
         clear();
         queue = null;
         size = 0;
         lock.notifyAll();
      }
   }

   public int size()
   {
      return size;
   }

   /**
    * Whether the queue is cancelled
    *
    * @return true when cancelled
    */
   public boolean isCancelled()
   {
      return queue == null;
   }

   /**
    *  A new node has been added at index index.
    *  Normalize the tree by moving the new node up the tree.
    *
    *  @return true if the tree was modified.
    */
   private boolean normalizeUp(int index)
   {
      // INV: assertExpr(index > 0);
      // INV: assertExpr(index <= size);
      // INV: assertExpr(queue[index] != null);
      if (index == 1)
         return false; // at root
      boolean ret = false;
      long t = queue[index].time;
      int p = index >> 1;
      while (queue[p].time > t)
      {
         // INV: assertExpr(queue[index].time == t);
         swap(p, index);
         ret = true;
         if (p == 1)
            break; // at root
         index = p;
         p >>= 1;
      }
      return ret;
   }

   /**
    * Swap two nodes in the tree.
    *
    * @param a the first index
    * @param b the second index
    */
   private void swap(int a, int b)
   {
      // INV: assertExpr(a > 0);
      // INV: assertExpr(a <= size);
      // INV: assertExpr(b > 0);
      // INV: assertExpr(b <= size);
      // INV: assertExpr(queue[a] != null);
      // INV: assertExpr(queue[b] != null);
      // INV: assertExpr(queue[a].index == a);
      // INV: assertExpr(queue[b].index == b);
      TimeoutExtImpl temp = queue[a];
      queue[a] = queue[b];
      queue[a].index = a;
      queue[b] = temp;
      queue[b].index = b;
   }

   /**
    * Remove a node from the tree and normalize.
    *
    * @param index the index in the queue
    * @return the removed node.
    */
   private TimeoutExtImpl removeNode(int index)
   {
      // INV: assertExpr(index > 0);
      // INV: assertExpr(index <= size);
      TimeoutExtImpl res = queue[index];
      // INV: assertExpr(res != null);
      // INV: assertExpr(res.index == index);
      if (index == size)
      {
         --size;
         queue[index] = null;
         return res;
      }
      swap(index, size); // Exchange removed node with last leaf node
      --size;
      // INV: assertExpr(res.index == size + 1);
      queue[res.index] = null;
      if (normalizeUp(index))
         return res; // Node moved up, so it shouldn't move down
      long t = queue[index].time;
      int c = index << 1;
      while (c <= size)
      {
         // INV: assertExpr(q[index].time == t);
         TimeoutExtImpl l = queue[c];
         // INV: assertExpr(l != null);
         // INV: assertExpr(l.index == c);
         if (c + 1 <= size)
         {
            // two children, swap with smallest
            TimeoutExtImpl r = queue[c + 1];
            // INV: assertExpr(r != null);
            // INV: assertExpr(r.index == c+1);
            if (l.time <= r.time)
            {
               if (t <= l.time)
                  break; // done
               swap(index, c);
               index = c;
            }
            else
            {
               if (t <= r.time)
                  break; // done
               swap(index, c + 1);
               index = c + 1;
            }
         }
         else
         { // one child
            if (t <= l.time)
               break; // done
            swap(index, c);
            index = c;
         }
         c = index << 1;
      }
      return res;
   }

   /**
    * Recursive cleanup of a TimeoutImpl
    *
    * @return null
    */
   private TimeoutExtImpl cleanupTimeoutExtImpl(TimeoutExtImpl timeout)
   {
      if (timeout != null)
         timeout.target = null;
      return null;
   }

   /**
    * Check invariants of the queue.
    */
   void checkTree()
   {
      assertExpr(size >= 0);
      assertExpr(size < queue.length);
      assertExpr(queue[0] == null);
      if (size > 0)
      {
         assertExpr(queue[1] != null);
         assertExpr(queue[1].index == 1);
         for (int i = 2; i <= size; ++i)
         {
            assertExpr(queue[i] != null);
            assertExpr(queue[i].index == i);
            assertExpr(queue[i >> 1].time <= queue[i].time); // parent fires first
         }
         for (int i = size + 1; i < queue.length; ++i)
            assertExpr(queue[i] == null);
      }
   }

   /**
    * Debugging helper.
    */
   private void assertExpr(boolean expr)
   {
      if (!expr)
         throw new IllegalStateException("***** assert failed *****");
   }

   /**
    *  Our private Timeout implementation.
    */
   private class TimeoutExtImpl implements TimeoutExt
   {
      /** Done */
      static final int DONE = -1;

      /** In timeout */
      static final int TIMEOUT = -2;

      /** Index in the queue */
      int index;

      /** Time of the timeout */
      long time;

      /** The timeout target */
      TimeoutTarget target;

      public long getTime()
      {
         return time;
      }

      public TimeoutTarget getTimeoutTarget()
      {
         return target;
      }

      public void done()
      {
         index = DONE;
      }

      public boolean cancel()
      {
         return remove(this);
      }
   }
}


/*
 * JBoss, Home of Professional Open Source
 * Copyright 2015, Red Hat, Inc., and individual contributors as indicated
 * by the @authors tag.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.jboss.util.loading;

import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLStreamHandlerFactory;

/**
 * A URL classloader that delegates to its parent, avoiding
 * synchronization.
 *
 * A standard flag is provided so it can be used as a parent class,
 * but later subclassed and to revert to standard class loading
 * if the subclass wants to load classes.
 *
 * @author Adrian Brock
 * @version $Revision$
 */
public class DelegatingClassLoader
   extends URLClassLoader
{
   /** The value returned by {@link #getURLs()}. */
   public static final URL[] EMPTY_URL_ARRAY = {};

   /** Whether to use standard loading */
   protected boolean standard = false;

   /** Cache the parent*/
   private ClassLoader parent = null;

   /**
    * Constructor
    *
    * @param parent the parent classloader, cannot be null.
    */
   public DelegatingClassLoader(ClassLoader parent)
   {
      super(EMPTY_URL_ARRAY, parent);
      if (parent == null)
         throw new IllegalArgumentException("No parent");
      this.parent = parent;
   }

   /**
    * Constructor
    *
    * @param parent the parent classloader, cannot be null.
    * @param factory the url stream factory.
    */
   public DelegatingClassLoader(ClassLoader parent, URLStreamHandlerFactory factory)
   {
      super(EMPTY_URL_ARRAY, parent, factory);
      if (parent == null)
         throw new IllegalArgumentException("No parent");
      this.parent = parent;
   }

   /**
    * Load a class, by asking the parent
    *
    * @param className the class name to load
    * @param resolve whether to link the class
    * @return the loaded class
    * @throws ClassNotFoundException when the class could not be found
    */
   protected Class<?> loadClass(String className, boolean resolve)
      throws ClassNotFoundException
   {
      // Revert to standard rules
      if (standard)
         return super.loadClass(className, resolve);

      // Ask the parent
      Class<?> clazz = null;
      try
      {
         clazz = parent.loadClass(className);
      }
      catch (ClassNotFoundException e)
      {
         // Not found in parent,
         // maybe it is a proxy registered against this classloader?
         clazz = findLoadedClass(className);
         if (clazz == null)
            throw e;
      }

      // Link the class
      if (resolve)
         resolveClass(clazz);

      return clazz;
   }
}


/*
 * JBoss, Home of Professional Open Source
 * Copyright 2015, Red Hat, Inc., and individual contributors as indicated
 * by the @authors tag.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.jboss.net.protocol;

import java.net.URL;
import java.net.URLStreamHandler;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.StringTokenizer;

import org.jboss.logging.Logger;

/**
 * A factory for loading URL protocol handlers.  This is based
 * on Sun's URL mechanism, in that <tt>Handler</tt> classes will be
 * searched for in the packages specified by the java.protocol.handler.pkgs
 * property are searched for classes matching the protocol + ".Handler"
 * classname. The default JBoss package "org.jboss.net.protocol" is searched
 * even if not specified in the java.protocol.handler.pkgs property.
 *
 *
This factory is installed by the default server implementaion
 * to ensure that protocol handlers not in the system classpath are
 * located. The thread context class is used first followed by the
 * Class.forName method.
 *

 *
 * @version <tt>$Revision$</tt>
 * @author  Jason Dillon
 * @author Scott.Stark@jboss.org
 */
@SuppressWarnings("unchecked")
public class URLStreamHandlerFactory implements java.net.URLStreamHandlerFactory
{
   /** Class logger. */
   private static final Logger log = Logger.getLogger(URLStreamHandlerFactory.class);

   /** The package prefix where JBoss protocol handlers live. */
   public static final String PACKAGE_PREFIX = "org.jboss.net.protocol";

   /** A map of protocol names to handlers. Since there can only be one
    URLStreamHandlerFactory installed, this is a static map that may be
    cleared.
    */
   private static Map handlerMap = Collections.synchronizedMap(new HashMap());

   /** This thread local is used to prevent recursion in the
    * createURLStreamHandler method. Resolving the protocol handler
    * class can end up creating a new URL which can loop back into
    * this factory with a stack like:
    *

      URLStreamHandlerFactory that use the TCL. See bug#669043
      createURLStreamHandler():146, URLStreamHandlerFactory.java
      getURLStreamHandler():1057, URL.java
      <init>():405, URL.java
      <init>():329, URL.java
      <init>():321, URL.java
      <init>():540, URLClassPath.java
      run():319, URLClassPath.java
      doPrivileged():-1, AccessController.java
      getLoader():308, URLClassPath.java
      getLoader():285, URLClassPath.java
      findResource():136, URLClassPath.java
      run():351, URLClassLoader.java
      doPrivileged():-1, AccessController.java
      findResource():348, URLClassLoader.java
      getResource():780, ClassLoader.java
      getResourceLocally():250, UnifiedClassLoader.java
      getResourceFromClassLoader():333, UnifiedLoaderRepository3.java
      getResource():243, UnifiedLoaderRepository3.java
      getResource():228, UnifiedClassLoader3.java

    So we detect recursion based on the protocol value matches the current
    createURLStreamHandlerProtocol setting.
   */
   private static ThreadLocal createURLStreamHandlerProtocol = new ThreadLocal();

   /** The current packages prefixes determined from the java.protocol.handler.pkgs
    property + the org.jboss.net.protocol default package.
    */
   private String[] handlerPkgs = {PACKAGE_PREFIX};

   /** The last java.protocol.handler.pkgs value. Used to determine if the
    java.protocol.handler.pkgs property has changed since handlerPkgs was
    last built.
    */
   private String lastHandlerPkgs = PACKAGE_PREFIX;

   /** A list of JBoss specific protocols for preloading. */
   public static final String PROTOCOLS[] = {
      "file",
      "resource"
   };

   /**
    * Preload the JBoss specific protocol handlers, so that URL knows about
    * them even if the handler factory is changed.
    */
   @SuppressWarnings("unused")
   public static void preload()
   {
      for (int i = 0; i < PROTOCOLS.length; i ++)
      {
         try
         {
            URL url = new URL(PROTOCOLS[i], "", -1, "");
            log.trace("Loaded protocol: " + PROTOCOLS[i]);
         }
         catch (Exception e)
         {
            log.warn("Failed to load protocol: " + PROTOCOLS[i], e);
         }
      }
   }

   /** Clear the current protocol to handler map. The map will be rebuilt
    as protocol handlers are requested.
    */
   public static void clear()
   {
      handlerMap.clear();
   }

   /** Search the handlerPkgs for URLStreamHandler classes matching the
    * pkg + protocol + ".Handler" naming convention.
    *
    * @param protocol The protocol to create a stream handler for
    * @return The protocol handler or null if not found
    */
   public URLStreamHandler createURLStreamHandler(final String protocol)
   {
      // Check the handler map
      URLStreamHandler handler = (URLStreamHandler) handlerMap.get(protocol);
      if( handler != null )
         return handler;

      // Validate that createURLStreamHandler is not recursing
      String prevProtocol = (String) createURLStreamHandlerProtocol.get();
      if( prevProtocol != null && prevProtocol.equals(protocol) )
         return null;
      createURLStreamHandlerProtocol.set(protocol);

      // See if the handler pkgs definition has changed
      checkHandlerPkgs();

      // Search the handlerPkgs for a matching protocol handler
      ClassLoader ctxLoader = Thread.currentThread().getContextClassLoader();
      for(int p = 0; p < handlerPkgs.length; p ++)
      {
         try
         {
            // Form the standard protocol handler class name
            String classname = handlerPkgs[p] + "." + protocol + ".Handler";
            Class<?> type = null;

            try
            {
               type = ctxLoader.loadClass(classname);
            }
            catch(ClassNotFoundException e)
            {
               // Try our class loader
               type = Class.forName(classname);
            }

            if( type != null )
            {
               handler = (URLStreamHandler) type.newInstance();
               handlerMap.put(protocol, handler);
               log.trace("Found protocol:"+protocol+" handler:"+handler);
            }
         }
         catch (Throwable ignore)
         {
         }
      }

      createURLStreamHandlerProtocol.set(null);
      return handler;
   }

   /** See if the java.protocol.handler.pkgs system property has changed
    and if it has, parse it to update the handlerPkgs array.
    */
   private synchronized void checkHandlerPkgs()
   {
      String handlerPkgsProp = System.getProperty("java.protocol.handler.pkgs");
      if( handlerPkgsProp != null && handlerPkgsProp.equals(lastHandlerPkgs) == false )
      {
         // Update the handlerPkgs[] from the handlerPkgsProp
         StringTokenizer tokeninzer = new StringTokenizer(handlerPkgsProp, "|");
         ArrayList<String> tmp = new ArrayList<String>();
         while( tokeninzer.hasMoreTokens() )
         {
            String pkg = tokeninzer.nextToken().intern();
            if( tmp.contains(pkg) == false )
               tmp.add(pkg);
         }
         // Include the JBoss default protocol handler pkg
         if( tmp.contains(PACKAGE_PREFIX) == false )
            tmp.add(PACKAGE_PREFIX);
         handlerPkgs = new String[tmp.size()];
         tmp.toArray(handlerPkgs);
         lastHandlerPkgs = handlerPkgsProp;
      }
   }

}


/*
 * JBoss, Home of Professional Open Source
 * Copyright 2015, Red Hat, Inc., and individual contributors as indicated
 * by the @authors tag.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.jboss.util.propertyeditor;

import org.jboss.util.NestedRuntimeException;

/**
 * A property editor for {@link java.lang.Class}.
 *
 * @version <tt>$Revision$</tt>
 * @author  Jason Dillon
 */
public class ClassEditor extends TextPropertyEditorSupport
{
   /**
    * Returns a Class for the input object converted to a string.
    *
    * @return a Class object
    *
    * @throws NestedRuntimeException   Failed to create Class instance.
    */
   public Object getValue()
   {
      try
      {
         ClassLoader loader = Thread.currentThread().getContextClassLoader();
         String classname = getAsText();
         Class<?> type = loader.loadClass(classname);

         return type;
      }
      catch (Exception e)
      {
         throw new NestedRuntimeException(e);
      }
   }
}


/*
 * JBoss, Home of Professional Open Source
 * Copyright 2015, Red Hat, Inc., and individual contributors as indicated
 * by the @authors tag.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.jboss.util.collection;

import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.io.Serializable;

/**
 * LazySet.
 * It's serializable if the elements are serializable.
 *
 * @param <T> the element type
 * @author Adrian Brock
 * @version $Revision$
 */
public class LazySet<T> implements Set<T>, Serializable
{
   /** The serialVersionUID */
   private static final long serialVersionUID = 1L;
   /** The delegate set */
   private Set<T> delegate = Collections.emptySet();

   /**
    * Create the set implementation
    *
    * @return the set
    */
   private Set<T> createImplementation()
   {
      if (delegate instanceof HashSet == false)
         return new HashSet<T>(delegate);
      return delegate;
   }

   public boolean add(T o)
   {
      if (delegate.isEmpty())
      {
         delegate = Collections.singleton(o);
         return true;
      }
      else
      {
         delegate = createImplementation();
         return delegate.add(o);
      }
   }

   public boolean addAll(Collection<? extends T> c)
   {
      delegate = createImplementation();
      return delegate.addAll(c);
   }

   public void clear()
   {
      delegate = Collections.emptySet();
   }

   public boolean contains(Object o)
   {
      return delegate.contains(o);
   }

   public boolean containsAll(Collection<?> c)
   {
      return delegate.containsAll(c);
   }

   public boolean isEmpty()
   {
      return delegate.isEmpty();
   }

   public Iterator<T> iterator()
   {
      return delegate.iterator();
   }

   public boolean remove(Object o)
   {
      delegate = createImplementation();
      return delegate.remove(o);
   }

   public boolean removeAll(Collection<?> c)
   {
      delegate = createImplementation();
      return delegate.removeAll(c);
   }

   public boolean retainAll(Collection<?> c)
   {
      delegate = createImplementation();
      return delegate.retainAll(c);
   }

   public int size()
   {
      return delegate.size();
   }

   public Object[] toArray()
   {
      return delegate.toArray();
   }

   public  U[] toArray(U[] a)
   {
      return delegate.toArray(a);
   }

   @Override
   public String toString()
   {
      return delegate.toString();
   }
}


/*
 * JBoss, Home of Professional Open Source
 * Copyright 2015, Red Hat, Inc., and individual contributors as indicated
 * by the @authors tag.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.jboss.util.stream;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;

import org.jboss.logging.Logger;

/**
 * A collection of stream related utility methods.
 *
 *
Exceptions that are thrown and not explicitly declared are ignored.
 *
 * @version <tt>$Revision$</tt>
 * @author  Jason Dillon
 */
public final class Streams
{
   private static final Logger log = Logger.getLogger(Streams.class);

   /////////////////////////////////////////////////////////////////////////
   //                               Closing                               //
   /////////////////////////////////////////////////////////////////////////

   /**
    * Attempt to close an <tt>InputStream</tt>.
    *
    * @param stream  <tt>InputStream</tt> to attempt to close.
    * @return        <tt>True</tt> if stream was closed (or stream was null),
    *                or <tt>false</tt> if an exception was thrown.
    */
   public static boolean close(final InputStream stream) {
      // do not attempt to close null stream, but return sucess
      if (stream == null) {
         return true;
      }

      boolean success = true;

      try {
         stream.close();
      }
      catch (IOException e) {
         success = false;
      }

      return success;
   }

   /**
    * Attempt to close an <tt>OutputStream</tt>.
    *
    * @param stream  <tt>OutputStream</tt> to attempt to close.
    * @return        <tt>True</tt> if stream was closed (or stream was null),
    *                or <tt>false</tt> if an exception was thrown.
    */
   public static boolean close(final OutputStream stream) {
      // do not attempt to close null stream, but return sucess
      if (stream == null) {
         return true;
      }

      boolean success = true;

      try {
         stream.close();
      }
      catch (IOException e) {
         success = false;
      }

      return success;
   }

   /**
    * Attempt to close an <tt>InputStream</tt> or <tt>OutputStream</tt>.
    *
    * @param stream  Stream to attempt to close.
    * @return        <tt>True</tt> if stream was closed (or stream was null),
    *                or <tt>false</tt> if an exception was thrown.
    *
    * @throws IllegalArgumentException    Stream is not an <tt>InputStream</tt>
    *                                     or <tt>OuputStream</tt>.
    */
   public static boolean close(final Object stream) {
      boolean success = false;

      if (stream instanceof InputStream) {
         success = close((InputStream)stream);
      }
      else if (stream instanceof OutputStream) {
         success = close((OutputStream)stream);
      }
      else {
         throw new IllegalArgumentException
            ("stream is not an InputStream or OutputStream");
      }

      return success;
   }

   /**
    * Attempt to close an array of <tt>InputStream</tt>s.
    *
    * @param streams Array of <tt>InputStream</tt>s to attempt to close.
    * @return        <tt>True</tt> if all streams were closed, or <tt>false</tt>
    *                if an exception was thrown.
    */
   public static boolean close(final InputStream[] streams) {
      boolean success = true;

      for (int i=0; i<streams.length; i++) {
         boolean rv = close(streams[i]);
         if (!rv) success = false;
      }

      return success;
   }

   /**
    * Attempt to close an array of <tt>OutputStream</tt>s.
    *
    * @param streams Array of <tt>OutputStream</tt>s to attempt to close.
    * @return        <tt>True</tt> if all streams were closed, or <tt>false</tt>
    *                if an exception was thrown.
    */
   public static boolean close(final OutputStream[] streams) {
      boolean success = true;

      for (int i=0; i<streams.length; i++) {
         boolean rv = close(streams[i]);
         if (!rv) success = false;
      }

      return success;
   }

   /**
    * Attempt to close an array of <tt>InputStream</tt>a and/or
    * <tt>OutputStream</tt>s.
    *
    * @param streams Array of streams to attempt to close.
    * @return        <tt>True</tt> if all streams were closed, or <tt>false</tt>
    *                if an exception was thrown.
    *
    * @throws IllegalArgumentException    Stream is not an <tt>InputStream</tt>
    *                                     or <tt>OuputStream</tt>.  Closing
    *                                     stops at the last valid stream
    *                                     object in this case.
    */
   public static boolean close(final Object[] streams) {
      boolean success = true;

      for (int i=0; i<streams.length; i++) {
         boolean rv = close(streams[i]);
         if (!rv) success = false;
      }

      return success;
   }

   /**
    * Attempt to flush and close an <tt>OutputStream</tt>.
    *
    * @param stream  <tt>OutputStream</tt> to attempt to flush and close.
    * @return        <tt>True</tt> if stream was flushed and closed, or
    *                <tt>false</tt> if an exception was thrown.
    */
   public static boolean fclose(final OutputStream stream) {
       return flush(stream) && close(stream);
   }

   /**
    * Attempt to flush and close an array of <tt>OutputStream</tt>s.
    *
    * @param streams  <tt>OutputStream</tt>s to attempt to flush and close.
    * @return         <tt>True</tt> if all streams were flushed and closed,
    *                 or <tt>false</tt> if an exception was thrown.
    */
   public static boolean fclose(final OutputStream[] streams) {
      boolean success = true;

      for (int i=0; i<streams.length; i++) {
         boolean rv = fclose(streams[i]);
         if (!rv) success = false;
      }

      return success;
   }

   /////////////////////////////////////////////////////////////////////////
   //                                Flushing                             //
   /////////////////////////////////////////////////////////////////////////

   /**
    * Attempt to flush an <tt>OutputStream</tt>.
    *
    * @param stream  <tt>OutputStream</tt> to attempt to flush.
    * @return        <tt>True</tt> if stream was flushed (or stream was null),
    *                or <tt>false</tt> if an exception was thrown.
    */
   public static boolean flush(final OutputStream stream) {
      // do not attempt to close null stream, but return sucess
      if (stream == null) {
         return true;
      }

      boolean success = true;

      try {
         stream.flush();
      }
      catch (IOException e) {
         success = false;
      }

      return success;
   }

   /**
    * Attempt to flush an array of <tt>OutputStream</tt>s.
    *
    * @param streams <tt>OutputStream</tt>s to attempt to flush.
    * @return        <tt>True</tt> if all streams were flushed, or <tt>false</tt>
    *                 if an exception was thrown.
    */
   public static boolean flush(final OutputStream[] streams) {
      boolean success = true;

      for (int i=0; i<streams.length; i++) {
         boolean rv = flush(streams[i]);
         if (!rv) success = false;
      }

      return success;
   }

   /////////////////////////////////////////////////////////////////////////
   //                                  Misc                               //
   /////////////////////////////////////////////////////////////////////////

   /** The default buffer size that will be used for buffered operations. */
   public static final int DEFAULT_BUFFER_SIZE = 2048;

   /**
    * Copy all of the bytes from the input stream to the output stream.
    *
    * @param input   Stream to read bytes from.
    * @param output  Stream to write bytes to.
    * @param buffer  The buffer to use while copying.
    * @return        The total number of bytes copied.
    *
    * @throws IOException  Failed to copy bytes.
    */
   public static long copy(final InputStream input,
                           final OutputStream output,
                           final byte buffer[])
      throws IOException
   {
      long total = 0;
      int read;

      boolean trace = log.isTraceEnabled();
      if (trace) {
         log.trace("copying " + input + " to " + output + " with buffer size: " + buffer.length);
      }

      while ((read = input.read(buffer)) != -1) {
         output.write(buffer, 0, read);
         total += read;

         if (trace) {
            log.trace("bytes read: " + read + "; total bytes read: " + total);
         }
      }

      return total;
   }

   /**
    * Copy all of the bytes from the input stream to the output stream.
    *
    * @param input   Stream to read bytes from.
    * @param output  Stream to write bytes to.
    * @param size    The size of the buffer to use while copying.
    * @return        The total number of bytes copied.
    *
    * @throws IOException  Failed to copy bytes.
    */
   public static long copy(final InputStream input,
                           final OutputStream output,
                           final int size)
      throws IOException
   {
      return copy(input, output, new byte[size]);
   }

   /**
    * Copy all of the bytes from the input stream to the output stream.
    *
    * @param input   Stream to read bytes from.
    * @param output  Stream to write bytes to.
    * @return        The total number of bytes copied.
    *
    * @throws IOException  Failed to copy bytes.
    */
   public static long copy(final InputStream input,
                           final OutputStream output)
      throws IOException
   {
      return copy(input, output, DEFAULT_BUFFER_SIZE);
   }

   /**
    * Copy all of the bytes from the input stream to the output stream
    * wrapping streams in buffers as needed.
    *
    * @param input   Stream to read bytes from.
    * @param output  Stream to write bytes to.
    * @return        The total number of bytes copied.
    *
    * @throws IOException  Failed to copy bytes.
    */
   public static long copyb(InputStream input,
                            OutputStream output)
      throws IOException
   {
      if (!(input instanceof BufferedInputStream)) {
         input = new BufferedInputStream(input);
      }

      if (!(output instanceof BufferedOutputStream)) {
         output = new BufferedOutputStream(output);
      }

      long bytes = copy(input, output, DEFAULT_BUFFER_SIZE);

      output.flush();

      return bytes;
   }

   /**
    * Copy a limited number of bytes from the input stream to the
    * output stream.
    *
    * @param input   Stream to read bytes from.
    * @param output  Stream to write bytes to.
    * @param buffer  The buffer to use while copying.
    * @param length  The maximum number of bytes to copy.
    * @return        The total number of bytes copied.
    *
    * @throws IOException  Failed to copy bytes.
    */
   public static long copySome(final InputStream input,
                               final OutputStream output,
                               final byte buffer[],
                               final long length)
      throws IOException
   {
      long total = 0;
      int read;
      int readLength;

      boolean trace = log.isTraceEnabled();

      // setup the initial readLength, if length is less than the buffer
      // size, then we only want to read that much
      readLength = Math.min((int)length, buffer.length);
      if (trace) {
         log.trace("initial read length: " + readLength);
      }

      while (readLength != 0 && (read = input.read(buffer, 0, readLength)) != -1)
      {
         if (trace) log.trace("read bytes: " + read);
         output.write(buffer, 0, read);
         total += read;
         if (trace) log.trace("total bytes read: " + total);

         // update the readLength
         readLength = Math.min((int)(length - total), buffer.length);
         if (trace) log.trace("next read length: " + readLength);
      }

      return total;
   }

   /**
    * Copy a limited number of bytes from the input stream to the
    * output stream.
    *
    * @param input   Stream to read bytes from.
    * @param output  Stream to write bytes to.
    * @param size    The size of the buffer to use while copying.
    * @param length  The maximum number of bytes to copy.
    * @return        The total number of bytes copied.
    *
    * @throws IOException  Failed to copy bytes.
    */
   public static long copySome(final InputStream input,
                               final OutputStream output,
                               final int size,
                               final long length)
      throws IOException
   {
      return copySome(input, output, new byte[size], length);
   }

   /**
    * Copy a limited number of bytes from the input stream to the
    * output stream.
    *
    * @param input   Stream to read bytes from.
    * @param output  Stream to write bytes to.
    * @param length  The maximum number of bytes to copy.
    * @return        The total number of bytes copied.
    *
    * @throws IOException  Failed to copy bytes.
    */
   public static long copySome(final InputStream input,
                               final OutputStream output,
                               final long length)
      throws IOException
   {
      return copySome(input, output, DEFAULT_BUFFER_SIZE, length);
   }
}


/*
 * JBoss, Home of Professional Open Source
 * Copyright 2015, Red Hat, Inc., and individual contributors as indicated
 * by the @authors tag.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.jboss.util;

import java.io.Serializable;

/**
 * A long integer counter class.
 *
 * @version <tt>$Revision$</tt>
 * @author  Jason Dillon
 */
public class LongCounter
   implements Serializable, Cloneable
{
   /** The serialVersionUID */
   private static final long serialVersionUID = 1615297462859270139L;
   /** The current count */
   private long count;

   /**
    * Construct a LongCounter with a starting value.
    *
    * @param count   Starting value for counter.
    */
   public LongCounter(final long count) {
      this.count = count;
   }

   /**
    * Construct a LongCounter.
    */
   public LongCounter() {}

   /**
    * Increment the counter. (Optional operation)
    *
    * @return  The incremented value of the counter.
    */
   public long increment() {
      return ++count;
   }

   /**
    * Decrement the counter. (Optional operation)
    *
    * @return  The decremented value of the counter.
    */
   public long decrement() {
      return --count;
   }

   /**
    * Return the current value of the counter.
    *
    * @return  The current value of the counter.
    */
   public long getCount() {
      return count;
   }

   /**
    * Reset the counter to zero. (Optional operation)
    */
   public void reset() {
      this.count = 0;
   }

   /**
    * Check if the given object is equal to this.
    *
    * @param obj  Object to test equality with.
    * @return     True if object is equal to this.
    */
   public boolean equals(final Object obj) {
      if (obj == this) return true;

      if (obj != null && obj.getClass() == getClass()) {
         return ((LongCounter)obj).count == count;
      }

      return false;
   }

   /**
    * Return a string representation of this.
    *
    * @return  A string representation of this.
    */
   public String toString() {
      return String.valueOf(count);
   }

   /**
    * Return a cloned copy of this object.
    *
    * @return  A cloned copy of this object.
    */
   public Object clone() {
      try {
         return super.clone();
      }
      catch (CloneNotSupportedException e) {
         throw new InternalError();
      }
   }

   /////////////////////////////////////////////////////////////////////////
   //                                Wrappers                             //
   /////////////////////////////////////////////////////////////////////////

   /**
    * Base wrapper class for other wrappers.
    */
   private static class Wrapper
      extends LongCounter
   {
      /** The serialVersionUID */
      private static final long serialVersionUID = 6720507617603163762L;
      /** The wrapped counter */
      protected final LongCounter counter;

      public Wrapper(final LongCounter counter) {
         this.counter = counter;
      }

      public long increment() {
         return counter.increment();
      }

      public long decrement() {
         return counter.decrement();
      }

      public long getCount() {
         return counter.getCount();
      }

      public void reset() {
         counter.reset();
      }

      public boolean equals(final Object obj) {
         return counter.equals(obj);
      }

      public String toString() {
         return counter.toString();
      }

      public Object clone() {
         return counter.clone();
      }
   }

   /**
    * Return a synchronized counter.
    *
    * @param counter    LongCounter to synchronize.
    * @return           Synchronized counter.
    */
   public static LongCounter makeSynchronized(final LongCounter counter)
   {
      return new Wrapper(counter) {
         /** The serialVersionUID */
         private static final long serialVersionUID = 8903330696503363758L;
            public synchronized long increment() {
               return this.counter.increment();
            }

            public synchronized long decrement() {
               return this.counter.decrement();
            }

            public synchronized long getCount() {
               return this.counter.getCount();
            }

            public synchronized void reset() {
               this.counter.reset();
            }

            public synchronized int hashCode() {
               return this.counter.hashCode();
            }

            public synchronized boolean equals(final Object obj) {
               return this.counter.equals(obj);
            }

            public synchronized String toString() {
               return this.counter.toString();
            }

            public synchronized Object clone() {
               return this.counter.clone();
            }
         };
   }

   /**
    * Returns a directional counter.
    *
    * @param counter       LongCounter to make directional.
    * @param increasing    True to create an increasing only
    *                      or false to create a decreasing only.
    * @return              A directional counter.
    */
   public static LongCounter makeDirectional(final LongCounter counter,
                                             final boolean increasing)
   {
      LongCounter temp;
      if (increasing) {
         temp = new Wrapper(counter) {
            /** The serialVersionUID */
            private static final long serialVersionUID = -8902748795144754375L;
            public long decrement() {
                  throw new UnsupportedOperationException();
               }

               public void reset() {
                  throw new UnsupportedOperationException();
               }
            };
      }
      else {
         temp = new Wrapper(counter) {
            /** The serialVersionUID */
            private static final long serialVersionUID = 2584758778978644599L;
               public long increment() {
                  throw new UnsupportedOperationException();
               }
            };
      }

      return temp;
   }
}


/*
 * JBoss, Home of Professional Open Source
 * Copyright 2015, Red Hat, Inc., and individual contributors as indicated
 * by the @authors tag.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// OASISXMLCatalogReader.java - Read XML Catalog files

package org.jboss.util.xml.catalog.readers;

import java.util.Enumeration;
import java.util.Stack;
import java.util.Vector;

import org.jboss.util.xml.catalog.Catalog;
import org.jboss.util.xml.catalog.CatalogEntry;
import org.jboss.util.xml.catalog.CatalogException;
import org.jboss.util.xml.catalog.helpers.PublicId;
import org.xml.sax.Attributes;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;

/**
 * Parse OASIS Entity Resolution Technical Committee
 * XML Catalog files.
 *
 * @see Catalog
 *
 * @author Norman Walsh
 * Norman.Walsh@Sun.COM
 *
 * @version 1.0
 */
@SuppressWarnings("unchecked")
public class OASISXMLCatalogReader extends SAXCatalogReader implements SAXCatalogParser {
  /** The catalog object needs to be stored by the object so that
   * SAX callbacks can use it.
   */
  protected Catalog catalog = null;

  /** The namespace name of OASIS ERTC catalogs */
  public static final String namespaceName = "urn:oasis:names:tc:entity:xmlns:xml:catalog";

  /** The namespace name of OASIS ERTC TR9401 catalog extension */
  public static final String tr9401NamespaceName = "urn:oasis:names:tc:entity:xmlns:tr9401:catalog";

  protected Stack baseURIStack = new Stack();
  protected Stack overrideStack = new Stack();
  protected Stack namespaceStack = new Stack();

  /** Set the current catalog. */
  public void setCatalog (Catalog catalog) {
    this.catalog = catalog;
    debug = catalog.getCatalogManager().debug;
  }

  /** @return the current catalog. */
  public Catalog getCatalog () {
    return catalog;
  }

  /**
   * Are we in an extension namespace?
   *
   * @return true if the current stack of open namespaces includes
   *               an extension namespace.
   */
  protected boolean inExtensionNamespace() {
    boolean inExtension = false;

    Enumeration elements = namespaceStack.elements();
    while (!inExtension && elements.hasMoreElements()) {
      String ns = (String) elements.nextElement();
      if (ns == null) {
	inExtension = true;
      } else {
	inExtension = (!ns.equals(tr9401NamespaceName)
		       && !ns.equals(namespaceName));
      }
    }

    return inExtension;
  }

  // ----------------------------------------------------------------------
  // Implement the SAX ContentHandler interface

  /** The SAX setDocumentLocator method does nothing. */
  public void setDocumentLocator (Locator locator) {
    return;
  }

  /** The SAX startDocument method does nothing. */
  public void startDocument ()
    throws SAXException {
    baseURIStack.push(catalog.getCurrentBase());
    overrideStack.push(catalog.getDefaultOverride());
    return;
  }

  /** The SAX endDocument method does nothing. */
  public void endDocument ()
    throws SAXException {
    return;
  }

  /**
   * The SAX startElement method recognizes elements
   * from the plain catalog format and instantiates CatalogEntry
   * objects for them.
   *
   * @param namespaceURI The namespace name of the element.
   * @param localName The local name of the element.
   * @param qName The QName of the element.
   * @param atts The list of attributes on the element.
   *
   * @see CatalogEntry
   */
  public void startElement (String namespaceURI,
			    String localName,
			    String qName,
			    Attributes atts)
    throws SAXException {

    int entryType = -1;
    Vector entryArgs = new Vector();

    namespaceStack.push(namespaceURI);

    boolean inExtension = inExtensionNamespace();

    if (namespaceURI != null && namespaceName.equals(namespaceURI)
	&& !inExtension) {
      // This is an XML Catalog entry

      if (atts.getValue("xml:base") != null) {
	String baseURI = atts.getValue("xml:base");
	entryType = Catalog.BASE;
	entryArgs.add(baseURI);
	baseURIStack.push(baseURI);

	debug.message(4, "xml:base", baseURI);

	try {
	  CatalogEntry ce = new CatalogEntry(entryType, entryArgs);
	  catalog.addEntry(ce);
	} catch (CatalogException cex) {
	  if (cex.getExceptionType() == CatalogException.INVALID_ENTRY_TYPE) {
	    debug.message(1, "Invalid catalog entry type", localName);
	  } else if (cex.getExceptionType() == CatalogException.INVALID_ENTRY) {
	    debug.message(1, "Invalid catalog entry (base)", localName);
	  }
	}

	entryType = -1;
	entryArgs = new Vector();

      } else {
	baseURIStack.push(baseURIStack.peek());
      }

      if ((localName.equals("catalog") || localName.equals("group"))
	  && atts.getValue("prefer") != null) {
	String override = atts.getValue("prefer");

	if (override.equals("public")) {
	  override = "yes";
	} else if (override.equals("system")) {
	  override = "no";
	} else {
	  debug.message(1,
			"Invalid prefer: must be 'system' or 'public'",
			localName);
	  override = catalog.getDefaultOverride();
	}

	entryType = Catalog.OVERRIDE;
	entryArgs.add(override);
	overrideStack.push(override);

	debug.message(4, "override", override);

	try {
	  CatalogEntry ce = new CatalogEntry(entryType, entryArgs);
	  catalog.addEntry(ce);
	} catch (CatalogException cex) {
	  if (cex.getExceptionType() == CatalogException.INVALID_ENTRY_TYPE) {
	    debug.message(1, "Invalid catalog entry type", localName);
	  } else if (cex.getExceptionType() == CatalogException.INVALID_ENTRY) {
	    debug.message(1, "Invalid catalog entry (override)", localName);
	  }
	}

	entryType = -1;
	entryArgs = new Vector();

      } else {
	overrideStack.push(overrideStack.peek());
      }

      if (localName.equals("delegatePublic")) {
	if (checkAttributes(atts, "publicIdStartString", "catalog")) {
	  entryType = Catalog.DELEGATE_PUBLIC;
	  entryArgs.add(atts.getValue("publicIdStartString"));
	  entryArgs.add(atts.getValue("catalog"));

	  debug.message(4, "delegatePublic",
			PublicId.normalize(atts.getValue("publicIdStartString")),
			atts.getValue("catalog"));
	}
      } else if (localName.equals("delegateSystem")) {
	if (checkAttributes(atts, "systemIdStartString", "catalog")) {
	  entryType = Catalog.DELEGATE_SYSTEM;
	  entryArgs.add(atts.getValue("systemIdStartString"));
	  entryArgs.add(atts.getValue("catalog"));

	  debug.message(4, "delegateSystem",
			atts.getValue("systemIdStartString"),
			atts.getValue("catalog"));
	}
      } else if (localName.equals("delegateURI")) {
	if (checkAttributes(atts, "uriStartString", "catalog")) {
	  entryType = Catalog.DELEGATE_URI;
	  entryArgs.add(atts.getValue("uriStartString"));
	  entryArgs.add(atts.getValue("catalog"));

	  debug.message(4, "delegateURI",
			atts.getValue("uriStartString"),
			atts.getValue("catalog"));
	}
      } else if (localName.equals("rewriteSystem")) {
	if (checkAttributes(atts, "systemIdStartString", "rewritePrefix")) {
	  entryType = Catalog.REWRITE_SYSTEM;
	  entryArgs.add(atts.getValue("systemIdStartString"));
	  entryArgs.add(atts.getValue("rewritePrefix"));

	  debug.message(4, "rewriteSystem",
			atts.getValue("systemIdStartString"),
			atts.getValue("rewritePrefix"));
	}
      } else if (localName.equals("rewriteURI")) {
	if (checkAttributes(atts, "uriStartString", "rewritePrefix")) {
	  entryType = Catalog.REWRITE_URI;
	  entryArgs.add(atts.getValue("uriStartString"));
	  entryArgs.add(atts.getValue("rewritePrefix"));

	  debug.message(4, "rewriteURI",
			atts.getValue("uriStartString"),
			atts.getValue("rewritePrefix"));
	}
      } else if (localName.equals("nextCatalog")) {
	if (checkAttributes(atts, "catalog")) {
	  entryType = Catalog.CATALOG;
	  entryArgs.add(atts.getValue("catalog"));

	  debug.message(4, "nextCatalog", atts.getValue("catalog"));
	}
      } else if (localName.equals("public")) {
	if (checkAttributes(atts, "publicId", "uri")) {
	  entryType = Catalog.PUBLIC;
	  entryArgs.add(atts.getValue("publicId"));
	  entryArgs.add(atts.getValue("uri"));

	  debug.message(4, "public",
			PublicId.normalize(atts.getValue("publicId")),
			atts.getValue("uri"));
	}
      } else if (localName.equals("system")) {
	if (checkAttributes(atts, "systemId", "uri")) {
	  entryType = Catalog.SYSTEM;
	  entryArgs.add(atts.getValue("systemId"));
	  entryArgs.add(atts.getValue("uri"));

	  debug.message(4, "system",
			atts.getValue("systemId"),
			atts.getValue("uri"));
	}
      } else if (localName.equals("uri")) {
	if (checkAttributes(atts, "name", "uri")) {
	  entryType = Catalog.URI;
	  entryArgs.add(atts.getValue("name"));
	  entryArgs.add(atts.getValue("uri"));

	  debug.message(4, "uri",
			atts.getValue("name"),
			atts.getValue("uri"));
	}
      } else if (localName.equals("catalog")) {
	// nop, start of catalog
      } else if (localName.equals("group")) {
	// nop, a group
      } else {
	// This is equivalent to an invalid catalog entry type
	debug.message(1, "Invalid catalog entry type", localName);
      }

      if (entryType >= 0) {
	try {
	  CatalogEntry ce = new CatalogEntry(entryType, entryArgs);
	  catalog.addEntry(ce);
	} catch (CatalogException cex) {
	  if (cex.getExceptionType() == CatalogException.INVALID_ENTRY_TYPE) {
	    debug.message(1, "Invalid catalog entry type", localName);
	  } else if (cex.getExceptionType() == CatalogException.INVALID_ENTRY) {
	    debug.message(1, "Invalid catalog entry", localName);
	  }
	}
      }
    }

    if (namespaceURI != null && tr9401NamespaceName.equals(namespaceURI)
	&& !inExtension) {
      // This is a TR9401 Catalog entry

      if (atts.getValue("xml:base") != null) {
	String baseURI = atts.getValue("xml:base");
	entryType = Catalog.BASE;
	entryArgs.add(baseURI);
	baseURIStack.push(baseURI);

	debug.message(4, "xml:base", baseURI);

	try {
	  CatalogEntry ce = new CatalogEntry(entryType, entryArgs);
	  catalog.addEntry(ce);
	} catch (CatalogException cex) {
	  if (cex.getExceptionType() == CatalogException.INVALID_ENTRY_TYPE) {
	    debug.message(1, "Invalid catalog entry type", localName);
	  } else if (cex.getExceptionType() == CatalogException.INVALID_ENTRY) {
	    debug.message(1, "Invalid catalog entry (base)", localName);
	  }
	}

	entryType = -1;
	entryArgs = new Vector();

      } else {
	baseURIStack.push(baseURIStack.peek());
      }

      if (localName.equals("doctype")) {
	entryType = catalog.DOCTYPE;
	entryArgs.add(atts.getValue("name"));
	entryArgs.add(atts.getValue("uri"));
      } else if (localName.equals("document")) {
	entryType = catalog.DOCUMENT;
	entryArgs.add(atts.getValue("uri"));
      } else if (localName.equals("dtddecl")) {
	entryType = catalog.DTDDECL;
	entryArgs.add(atts.getValue("publicId"));
	entryArgs.add(atts.getValue("uri"));
      } else if (localName.equals("entity")) {
	entryType = Catalog.ENTITY;
	entryArgs.add(atts.getValue("name"));
	entryArgs.add(atts.getValue("uri"));
      } else if (localName.equals("linktype")) {
	entryType = Catalog.LINKTYPE;
	entryArgs.add(atts.getValue("name"));
	entryArgs.add(atts.getValue("uri"));
      } else if (localName.equals("notation")) {
	entryType = Catalog.NOTATION;
	entryArgs.add(atts.getValue("name"));
	entryArgs.add(atts.getValue("uri"));
      } else if (localName.equals("sgmldecl")) {
	entryType = Catalog.SGMLDECL;
	entryArgs.add(atts.getValue("uri"));
      } else {
	// This is equivalent to an invalid catalog entry type
	debug.message(1, "Invalid catalog entry type", localName);
      }

      if (entryType >= 0) {
	try {
	  CatalogEntry ce = new CatalogEntry(entryType, entryArgs);
	  catalog.addEntry(ce);
	} catch (CatalogException cex) {
	  if (cex.getExceptionType() == CatalogException.INVALID_ENTRY_TYPE) {
	    debug.message(1, "Invalid catalog entry type", localName);
	  } else if (cex.getExceptionType() == CatalogException.INVALID_ENTRY) {
	    debug.message(1, "Invalid catalog entry", localName);
	  }
	}
      }
    }
  }

  public boolean checkAttributes (Attributes atts, String attName) {
    if (atts.getValue(attName) == null) {
      debug.message(1, "Error: required attribute " + attName + " missing.");
      return false;
    } else {
      return true;
    }
  }

  public boolean checkAttributes (Attributes atts,
				  String attName1,
				  String attName2) {
    return checkAttributes(atts, attName1)
      && checkAttributes(atts, attName2);
  }

  /** The SAX endElement method does nothing. */
  public void endElement (String namespaceURI,
			  String localName,
			  String qName)
    throws SAXException {

    int entryType = -1;
    Vector entryArgs = new Vector();

    boolean inExtension = inExtensionNamespace();

    if (namespaceURI != null
	&& !inExtension
	&& (namespaceName.equals(namespaceURI)
	    || tr9401NamespaceName.equals(namespaceURI))) {

      String popURI = (String) baseURIStack.pop();
      String baseURI = (String) baseURIStack.peek();

      if (!baseURI.equals(popURI)) {
	entryType = catalog.BASE;
	entryArgs.add(baseURI);

	debug.message(4, "(reset) xml:base", baseURI);

	try {
	  CatalogEntry ce = new CatalogEntry(entryType, entryArgs);
	  catalog.addEntry(ce);
	} catch (CatalogException cex) {
	  if (cex.getExceptionType() == CatalogException.INVALID_ENTRY_TYPE) {
	    debug.message(1, "Invalid catalog entry type", localName);
	  } else if (cex.getExceptionType() == CatalogException.INVALID_ENTRY) {
	    debug.message(1, "Invalid catalog entry (rbase)", localName);
	  }
	}
      }
    }

    if (namespaceURI != null && namespaceName.equals(namespaceURI)
	&& !inExtension) {
      if (localName.equals("catalog") || localName.equals("group")) {
	String popOverride = (String) overrideStack.pop();
	String override = (String) overrideStack.peek();

	if (!override.equals(popOverride)) {
	  entryType = catalog.OVERRIDE;
	  entryArgs.add(override);
	  overrideStack.push(override);

	  debug.message(4, "(reset) override", override);

	  try {
	    CatalogEntry ce = new CatalogEntry(entryType, entryArgs);
	    catalog.addEntry(ce);
	  } catch (CatalogException cex) {
	    if (cex.getExceptionType() == CatalogException.INVALID_ENTRY_TYPE) {
	      debug.message(1, "Invalid catalog entry type", localName);
	    } else if (cex.getExceptionType() == CatalogException.INVALID_ENTRY) {
	      debug.message(1, "Invalid catalog entry (roverride)", localName);
	    }
	  }
	}
      }
    }

    namespaceStack.pop();

    return;
  }

  /** The SAX characters method does nothing. */
  public void characters (char ch[], int start, int length)
    throws SAXException {
    return;
  }

  /** The SAX ignorableWhitespace method does nothing. */
  public void ignorableWhitespace (char ch[], int start, int length)
    throws SAXException {
    return;
  }

  /** The SAX processingInstruction method does nothing. */
  public void processingInstruction (String target, String data)
    throws SAXException {
    return;
  }

  /** The SAX skippedEntity method does nothing. */
  public void skippedEntity (String name)
    throws SAXException {
    return;
  }

  /** The SAX startPrefixMapping method does nothing. */
  public void startPrefixMapping(String prefix, String uri)
    throws SAXException {
    return;
  }

  /** The SAX endPrefixMapping method does nothing. */
  public void endPrefixMapping(String prefix)
    throws SAXException {
    return;
  }

}


/*
 * JBoss, Home of Professional Open Source
 * Copyright 2015, Red Hat, Inc., and individual contributors as indicated
 * by the @authors tag.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.jboss.util.stream;

import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.OutputStream;

/**
 * A buffered output stream that notifies every "chunk"
 *
 * @version <tt>$Revision$</tt>
 * @author  Adrian Brock
 */
public class NotifyingBufferedOutputStream
   extends BufferedOutputStream
{
   /**
    * The number of bytes between notifications
    */
   int chunkSize;

   /**
    * The number of bytes written in the current chunk
    */
   int chunk = 0;

   /**
    * The listener notified every chunk
    */
   StreamListener listener;

   /**
    * Construct a notifying buffered outputstream.

    * The listener is notified once every chunk.
    *
    * @param os the output stream to be buffered
    * @param size the buffer size
    * @param chunkSize the chunk size
    * @param listener
    * @exception IllegalArgumentException for a size <= 0 or chunkSize <= size or a null listener
    */
   public NotifyingBufferedOutputStream(OutputStream os, int size, int chunkSize, StreamListener listener)
   {
      super(os, size);
      if (chunkSize <= size)
         throw new IllegalArgumentException("chunkSize must be bigger than the buffer");
      this.chunkSize = chunkSize;
      this.listener = listener;
   }

   public void setStreamListener(StreamListener listener)
   {
      this.listener = listener;
   }

   public void write(int b)
      throws IOException
   {
      super.write(b);
      checkNotification(1);
   }

   public void write(byte[] b, int off, int len)
      throws IOException
   {
      super.write(b, off, len);
      checkNotification(len);
   }

   /**
    * Checks whether a notification is required and
    * notifies as appropriate
    *
    * @param result the number of bytes written
    */
   public void checkNotification(int result)
   {
      // Is a notification required?
      chunk += result;
      if (chunk >= chunkSize)
      {
         if (listener != null)
            listener.onStreamNotification(this, chunk);

         // Start a new chunk
         chunk = 0;
      }
   }
}