/*
 * 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.
 */

// XCatalogReader.java - Read XML Catalog files

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

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.*;

import javax.xml.parsers.*;

/**
 * Parse "xcatalog" XML Catalog files, this is the XML Catalog format
 * developed by John Cowan and supported by Apache.
 *
 * @see Catalog
 *
 * @author Norman Walsh
 * Norman.Walsh@Sun.COM
 *
 * @version 1.0
 */
@SuppressWarnings("unchecked")
public class XCatalogReader 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;

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

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

  /** The constructor
 * @param parserFactory */
  public XCatalogReader(SAXParserFactory parserFactory) {
    super(parserFactory);
  }

  // ----------------------------------------------------------------------
  // Implement the SAX DocumentHandler interface

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

  /** The SAX startDocument method does nothing. */
  public void startDocument ()
    throws SAXException {
    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();

    if (localName.equals("Base")) {
      entryType = catalog.BASE;
      entryArgs.add(atts.getValue("HRef"));

      catalog.getCatalogManager().debug.message(4, "Base", atts.getValue("HRef"));
    } else if (localName.equals("Delegate")) {
      entryType = catalog.DELEGATE_PUBLIC;
      entryArgs.add(atts.getValue("PublicId"));
      entryArgs.add(atts.getValue("HRef"));

      catalog.getCatalogManager().debug.message(4, "Delegate",
		    PublicId.normalize(atts.getValue("PublicId")),
		    atts.getValue("HRef"));
    } else if (localName.equals("Extend")) {
      entryType = catalog.CATALOG;
      entryArgs.add(atts.getValue("HRef"));

      catalog.getCatalogManager().debug.message(4, "Extend", atts.getValue("HRef"));
    } else if (localName.equals("Map")) {
      entryType = catalog.PUBLIC;
      entryArgs.add(atts.getValue("PublicId"));
      entryArgs.add(atts.getValue("HRef"));

      catalog.getCatalogManager().debug.message(4, "Map",
		    PublicId.normalize(atts.getValue("PublicId")),
		    atts.getValue("HRef"));
    } else if (localName.equals("Remap")) {
      entryType = catalog.SYSTEM;
      entryArgs.add(atts.getValue("SystemId"));
      entryArgs.add(atts.getValue("HRef"));

      catalog.getCatalogManager().debug.message(4, "Remap",
		    atts.getValue("SystemId"),
		    atts.getValue("HRef"));
    } else if (localName.equals("XMLCatalog")) {
      // nop, start of catalog
    } else {
      // This is equivalent to an invalid catalog entry type
      catalog.getCatalogManager().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) {
	  catalog.getCatalogManager().debug.message(1, "Invalid catalog entry type", localName);
	} else if (cex.getExceptionType() == CatalogException.INVALID_ENTRY) {
	  catalog.getCatalogManager().debug.message(1, "Invalid catalog entry", localName);
	}
      }
    }
    }

    /** The SAX endElement method does nothing. */
    public void endElement (String namespaceURI,
			    String localName,
			    String qName)
      throws SAXException {
      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;
  }
}


/*
 * 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;

/**
 * This class runs in a single thread {@link TimerTask}s that have been
 * scheduled.

 * A similar class is present in java.util package of jdk version >= 1.3;
 * for compatibility with jdk 1.2 it is reimplemented here.
 *
 * @see TimerTask
 * @author Simone Bordet
 * @version $Revision$
 */
public class TimerQueue
   extends WorkerQueue
{
   // Constants -----------------------------------------------------

   // Attributes ----------------------------------------------------
   /* This member is accessed only from putJob and getJob, that will
      then guarantee necessary synchronization on it */
   private Heap m_heap;

   // Static --------------------------------------------------------

   // Constructors --------------------------------------------------
   /**
    * Creates a new timer queue with default thread name of "TimerTask Thread"
    */
   public TimerQueue()
   {
      this("TimerTask Thread");
   }
   /**
    * Creates a new timer queue with the specified thread name
    * @param threadName
    */
   public TimerQueue(String threadName)
   {
      super(threadName);
      m_heap = new Heap();
   }

   // Public --------------------------------------------------------
   /**
    * Schedules the given TimerTask for immediate execution.
    * @param t
    */
   public void schedule(TimerTask t)
   {
      schedule(t, 0);
   }
   /**
    * Schedule the given TimerTask to be executed after delay
    * milliseconds.
    * @param t
    * @param delay
    */
   public void schedule(TimerTask t, long delay)
   {
      if (t == null) throw new IllegalArgumentException("Can't schedule a null TimerTask");
      if (delay < 0) delay = 0;
      t.setNextExecutionTime(System.currentTimeMillis() + delay);
      putJob(t);
   }

   // Z implementation ----------------------------------------------

   // WorkerQueue overrides ---------------------------------------------------
   protected void putJobImpl(Executable task)
   {
      m_heap.insert(task);
      ((TimerTask)task).setState(TimerTask.SCHEDULED);
      notifyAll();
   }
   protected Executable getJobImpl() throws InterruptedException
   {
      while (m_heap.peek() == null)
      {
         wait();
      }
      TimerTask task = (TimerTask)m_heap.extract();
      switch (task.getState())
      {
       case TimerTask.CANCELLED:
       case TimerTask.EXECUTED:
          //don't hold onto last dead task if we wait.
          task = null;
          return getJobImpl();
       case TimerTask.NEW:
       case TimerTask.SCHEDULED:
          return task;
       default:
          throw new IllegalStateException("TimerTask has an illegal state");
      }
   }
   protected Runnable createQueueLoop()
   {
      return new TimerTaskLoop();
   }
   protected void clear()
   {
      super.clear();
      synchronized (this)
      {
         m_heap.clear();
      }
   }

   // Package protected ---------------------------------------------

   // Protected -----------------------------------------------------

   // Private -------------------------------------------------------

   // Inner classes -------------------------------------------------
   /**
    * Class that loops getting the next job to be executed and then
    * executing it, in the timer task thread.
    */
   protected class TimerTaskLoop implements Runnable
   {
      public void run()
      {
         try
         {
            while (true)
            {
               try
               {
                  // Wait for the first job
                  TimerTask task = (TimerTask)getJob();
                  long now = System.currentTimeMillis();
                  long executionTime = task.getNextExecutionTime();
                  long timeToWait = executionTime - now;
                  boolean runTask = timeToWait <= 0;
                  if (!runTask)
                  {
                     // Entering here when a new job is scheduled but
                     // it's not yet time to run the first one;
                     // the job was extracted from the heap, reschedule
                     putJob(task);
                     Object mutex = TimerQueue.this;
                     synchronized (mutex)
                     {
                        // timeToWait is always strictly > 0, so I don't wait forever
                        mutex.wait(timeToWait);
                     }
                  }
                  else
                  {
                     if (task.isPeriodic())
                     {
                        // Reschedule with the new time
                        task.setNextExecutionTime(executionTime + task.getPeriod());
                        putJob(task);
                     }
                     else
                     {
                        // The one-shot task is already removed from
                        // the heap, mark it as executed
                        task.setState(TimerTask.EXECUTED);
                     }
                     // Run it !
                     task.execute();
                  }
               }
               catch (InterruptedException x)
               {
                  // Exit the thread
                  break;
               }
               catch (Exception e)
               {
                  ThrowableHandler.add(ThrowableHandler.Type.ERROR, e);
               }
            }
         }
         finally	{clear();}
      }
   }
}


/*
 * 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 {@link Integer}.
 *
 * @author Scott.Stark@jboss.org
 * @version $Revision$
 */
public class FloatEditor extends PropertyEditorSupport
{
   /**
    * Map the argument text into and Integer using Integer.valueOf.
    */
   public void setAsText(final String text)
   {
      Object newValue = Float.valueOf(text);
      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.
 */

// Resolver.java - Represents an extension of OASIS Open Catalog files.

package org.jboss.util.xml.catalog;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.Enumeration;
import java.util.Vector;

import javax.xml.parsers.SAXParserFactory;

import org.jboss.util.xml.catalog.readers.ExtendedXMLCatalogReader;
import org.jboss.util.xml.catalog.readers.OASISXMLCatalogReader;
import org.jboss.util.xml.catalog.readers.SAXCatalogReader;
import org.jboss.util.xml.catalog.readers.TR9401CatalogReader;
import org.jboss.util.xml.catalog.readers.XCatalogReader;

/**
 * An extension to OASIS Open Catalog files, this class supports
 * suffix-based matching and an external RFC2483 resolver.
 *
 * @see Catalog
 *
 * @author Norman Walsh
 * Norman.Walsh@Sun.COM
 *
 * @version 1.0
 */
@SuppressWarnings("unchecked")
public class Resolver extends Catalog {
  /**
   * The URISUFFIX Catalog Entry type.
   *
   *
URI suffix entries match URIs that end in a specified suffix.

   */
  public static final int URISUFFIX = CatalogEntry.addEntryType("URISUFFIX", 2);

  /**
   * The SYSTEMSUFFIX Catalog Entry type.
   *
   *
System suffix entries match system identifiers that end in a
   * specified suffix.

   */
  public static final int SYSTEMSUFFIX = CatalogEntry.addEntryType("SYSTEMSUFFIX", 2);

  /**
   * The RESOLVER Catalog Entry type.
   *
   *
A hook for providing support for web-based backup resolvers.

   */
  public static final int RESOLVER = CatalogEntry.addEntryType("RESOLVER", 1);

  /**
   * The SYSTEMREVERSE Catalog Entry type.
   *
   *
This is a bit of a hack. There's no actual SYSTEMREVERSE entry,
   * but this entry type is used to indicate that a reverse lookup is
   * being performed. (This allows the Resolver to implement
   * RFC2483 I2N and I2NS.)
   */
  public static final int SYSTEMREVERSE
    = CatalogEntry.addEntryType("SYSTEMREVERSE", 1);

  /**
   * Setup readers.
   */
  @SuppressWarnings("deprecation")
  public void setupReaders() {
    SAXParserFactory spf = SAXParserFactory.newInstance();
    spf.setNamespaceAware(true);
    spf.setValidating(false);

    SAXCatalogReader saxReader = new SAXCatalogReader(spf);

    saxReader.setCatalogParser(null, "XMLCatalog",
			       XCatalogReader.class.getName());

    saxReader.setCatalogParser(OASISXMLCatalogReader.namespaceName,
			       "catalog",
			       ExtendedXMLCatalogReader.class.getName());

    addReader("application/xml", saxReader);

    TR9401CatalogReader textReader = new TR9401CatalogReader();
    addReader("text/plain", textReader);
  }

  /**
   * Cleanup and process a Catalog entry.
   *
   *
This method processes each Catalog entry, changing mapped
   * relative system identifiers into absolute ones (based on the current
   * base URI), and maintaining other information about the current
   * catalog.

   *
   * @param entry The CatalogEntry to process.
   */
  public void addEntry(CatalogEntry entry) {
    int type = entry.getEntryType();

    if (type == URISUFFIX) {
      String suffix = normalizeURI(entry.getEntryArg(0));
      String fsi = makeAbsolute(normalizeURI(entry.getEntryArg(1)));

      entry.setEntryArg(1, fsi);

      catalogManager.debug.message(4, "URISUFFIX", suffix, fsi);
    } else if (type == SYSTEMSUFFIX) {
      String suffix = normalizeURI(entry.getEntryArg(0));
      String fsi = makeAbsolute(normalizeURI(entry.getEntryArg(1)));

      entry.setEntryArg(1, fsi);

      catalogManager.debug.message(4, "SYSTEMSUFFIX", suffix, fsi);
    }

    super.addEntry(entry);
  }

  /**
   * Return the applicable URI.
   *
   *
If a URI entry exists in the Catalog
   * for the URI specified, return the mapped value.

   *
   *
In the Resolver (as opposed to the Catalog) class, if the
   * URI isn't found by the usual algorithm, URISUFFIX entries are
   * considered.

   *
   *
URI comparison is case sensitive.

   *
   * @param uri The URI to locate in the catalog.
   *
   * @return The resolved URI.
   *
   * @throws MalformedURLException The system identifier of a
   * subordinate catalog cannot be turned into a valid URL.
   * @throws IOException Error reading subordinate catalog file.
   */
  public String resolveURI(String uri)
    throws MalformedURLException, IOException {

    String resolved = super.resolveURI(uri);
    if (resolved != null) {
      return resolved;
    }

    Enumeration enumt = catalogEntries.elements();
    while (enumt.hasMoreElements()) {
      CatalogEntry e = (CatalogEntry) enumt.nextElement();
      if (e.getEntryType() == RESOLVER) {
	resolved = resolveExternalSystem(uri, e.getEntryArg(0));
	if (resolved != null) {
	  return resolved;
	}
      } else if (e.getEntryType() == URISUFFIX) {
	String suffix = e.getEntryArg(0);
	String result = e.getEntryArg(1);

	if (suffix.length() <= uri.length()
	    && uri.substring(uri.length()-suffix.length()).equals(suffix)) {
	  return result;
	}
      }
    }

    // Otherwise, look in the subordinate catalogs
    return resolveSubordinateCatalogs(Catalog.URI,
				      null,
				      null,
				      uri);
  }

  /**
   * Return the applicable SYSTEM system identifier, resorting
   * to external RESOLVERs if necessary.
   *
   *
If a SYSTEM entry exists in the Catalog
   * for the system ID specified, return the mapped value.

   *
   *
In the Resolver (as opposed to the Catalog) class, if the
   * URI isn't found by the usual algorithm, SYSTEMSUFFIX entries are
   * considered.

   *
   *
On Windows-based operating systems, the comparison between
   * the system identifier provided and the SYSTEM entries in the
   * Catalog is case-insensitive.

   *
   * @param systemId The system ID to locate in the catalog.
   *
   * @return The system identifier to use for systemId.
   *
   * @throws MalformedURLException The formal system identifier of a
   * subordinate catalog cannot be turned into a valid URL.
   * @throws IOException Error reading subordinate catalog file.
   */
  public String resolveSystem(String systemId)
    throws MalformedURLException, IOException {

    String resolved = super.resolveSystem(systemId);
    if (resolved != null) {
      return resolved;
    }

    Enumeration enumt = catalogEntries.elements();
    while (enumt.hasMoreElements()) {
      CatalogEntry e = (CatalogEntry) enumt.nextElement();
      if (e.getEntryType() == RESOLVER) {
	resolved = resolveExternalSystem(systemId, e.getEntryArg(0));
	if (resolved != null) {
	  return resolved;
	}
      } else if (e.getEntryType() == SYSTEMSUFFIX) {
	String suffix = e.getEntryArg(0);
	String result = e.getEntryArg(1);

	if (suffix.length() <= systemId.length()
	    && systemId.substring(systemId.length()-suffix.length()).equals(suffix)) {
	  return result;
	}
      }
    }

    return resolveSubordinateCatalogs(Catalog.SYSTEM,
				      null,
				      null,
				      systemId);
  }

  /**
   * Return the applicable PUBLIC or SYSTEM identifier, resorting
   * to external resolvers if necessary.
   *
   *
This method searches the Catalog and returns the system
   * identifier specified for the given system or
   * public identifiers. If
   * no appropriate PUBLIC or SYSTEM entry is found in the Catalog,
   * null is returned.

   *
   *
Note that a system or public identifier in the current catalog
   * (or subordinate catalogs) will be used in preference to an
   * external resolver. Further, if a systemId is present, the external
   * resolver(s) will be queried for that before the publicId.

   *
   * @param publicId The public identifier to locate in the catalog.
   * Public identifiers are normalized before comparison.
   * @param systemId The nominal system identifier for the entity
   * in question (as provided in the source document).
   *
   * @throws MalformedURLException The formal system identifier of a
   * subordinate catalog cannot be turned into a valid URL.
   * @throws IOException Error reading subordinate catalog file.
   *
   * @return The system identifier to use.
   * Note that the nominal system identifier is not returned if a
   * match is not found in the catalog, instead null is returned
   * to indicate that no match was found.
   */
  public String resolvePublic(String publicId, String systemId)
    throws MalformedURLException, IOException {

    String resolved = super.resolvePublic(publicId, systemId);
    if (resolved != null) {
      return resolved;
    }

    Enumeration enumt = catalogEntries.elements();
    while (enumt.hasMoreElements()) {
      CatalogEntry e = (CatalogEntry) enumt.nextElement();
      if (e.getEntryType() == RESOLVER) {
	if (systemId != null) {
	  resolved = resolveExternalSystem(systemId,
					   e.getEntryArg(0));
	  if (resolved != null) {
	    return resolved;
	  }
	}
	resolved = resolveExternalPublic(publicId, e.getEntryArg(0));
	if (resolved != null) {
	  return resolved;
	}
      }
    }

    return resolveSubordinateCatalogs(Catalog.PUBLIC,
				      null,
				      publicId,
				      systemId);
  }

    /**
     * Query an external RFC2483 resolver for a system identifier.
     *
     * @param systemId The system ID to locate.
     * @param resolver The name of the resolver to use.
     *
     * @return The system identifier to use for the systemId.
     * @throws MalformedURLException
     * @throws IOException
     */
    protected String resolveExternalSystem(String systemId, String resolver)
	throws MalformedURLException, IOException {
	Resolver r = queryResolver(resolver, "i2l", systemId, null);
	if (r != null) {
	    return r.resolveSystem(systemId);
	} else {
	    return null;
	}
    }

    /**
     * Query an external RFC2483 resolver for a public identifier.
     *
     * @param publicId The system ID to locate.
     * @param resolver The name of the resolver to use.
     *
     * @throws MalformedURLException
     * @throws IOException
     * @return The system identifier to use for the systemId.
     */
    protected String resolveExternalPublic(String publicId, String resolver)
	throws MalformedURLException, IOException {
	Resolver r = queryResolver(resolver, "fpi2l", publicId, null);
	if (r != null) {
	    return r.resolvePublic(publicId, null);
	} else {
	    return null;
	}
    }

    /**
     * Query an external RFC2483 resolver.
     *
     * @param resolver The URL of the RFC2483 resolver.
     * @param command The command to send the resolver.
     * @param arg1 The first argument to the resolver.
     * @param arg2 The second argument to the resolver, usually null.
     *
     * @return The Resolver constructed.
     */
    protected Resolver queryResolver(String resolver,
				     String command,
				     String arg1,
				     String arg2) {
	String RFC2483 = resolver + "?command=" + command
	    + "&format=tr9401&uri=" + arg1
	    + "&uri2=" + arg2;

	try {
	    URL url = new URL(RFC2483);

	    URLConnection urlCon = url.openConnection();

	    urlCon.setUseCaches(false);

	    Resolver r = (Resolver) newCatalog();

	    String cType = urlCon.getContentType();

	    // I don't care about the character set or subtype
	    if (cType.indexOf(";") > 0) {
		cType = cType.substring(0, cType.indexOf(";"));
	    }

	    r.parseCatalog(cType, urlCon.getInputStream());

	    return r;
	} catch (CatalogException cex) {
	  if (cex.getExceptionType() == CatalogException.UNPARSEABLE) {
	    catalogManager.debug.message(1, "Unparseable catalog: " + RFC2483);
	  } else if (cex.getExceptionType()
		     == CatalogException.UNKNOWN_FORMAT) {
	    catalogManager.debug.message(1, "Unknown catalog format: " + RFC2483);
	  }
	  return null;
	} catch (MalformedURLException mue) {
	    catalogManager.debug.message(1, "Malformed resolver URL: " + RFC2483);
	    return null;
	} catch (IOException ie) {
	    catalogManager.debug.message(1, "I/O Exception opening resolver: " + RFC2483);
	    return null;
	}
    }

    /**
     * Append two vectors, returning the result.
     *
     * @param vec The first vector
     * @param appvec The vector to be appended
     * @return The vector vec, with appvec's elements appended to it
     */
    private Vector appendVector(Vector vec, Vector appvec) {
	if (appvec != null) {
	    for (int count = 0; count < appvec.size(); count++) {
		vec.addElement(appvec.elementAt(count));
	    }
	}
	return vec;
    }

    /**
     * Find the URNs for a given system identifier in all catalogs.
     *
     * @param systemId The system ID to locate.
     *
     * @throws MalformedURLException
     * @throws IOException
     * @return A vector of URNs that map to the systemId.
     */
    public Vector resolveAllSystemReverse(String systemId)
	throws MalformedURLException, IOException {
	Vector resolved = new Vector();

	// If there's a SYSTEM entry in this catalog, use it
	if (systemId != null) {
	    Vector localResolved = resolveLocalSystemReverse(systemId);
	    resolved = appendVector(resolved, localResolved);
	}

	// Otherwise, look in the subordinate catalogs
	Vector subResolved = resolveAllSubordinateCatalogs(SYSTEMREVERSE,
							   null,
							   null,
							   systemId);

	return appendVector(resolved, subResolved);
    }

    /**
     * Find the URN for a given system identifier.
     *
     * @param systemId The system ID to locate.
     *
     * @throws MalformedURLException
     * @throws IOException
     * @return A (single) URN that maps to the systemId.
     */
    public String resolveSystemReverse(String systemId)
	throws MalformedURLException, IOException {
	Vector resolved = resolveAllSystemReverse(systemId);
	if (resolved != null && resolved.size() > 0) {
	    return (String) resolved.elementAt(0);
	} else {
	    return null;
	}
    }

    /**
     * Return the applicable SYSTEM system identifiers.
     *
     *
If one or more SYSTEM entries exists in the Catalog
     * for the system ID specified, return the mapped values.

     *
     *
The caller is responsible for doing any necessary
     * normalization of the system identifier before calling
     * this method. For example, a relative system identifier in
     * a document might be converted to an absolute system identifier
     * before attempting to resolve it.

     *
     *
Note that this function will force all subordinate catalogs
     * to be loaded.

     *
     *
On Windows-based operating systems, the comparison between
     * the system identifier provided and the SYSTEM entries in the
     * Catalog is case-insensitive.

     *
     * @param systemId The system ID to locate in the catalog.
     *
     * @return The system identifier to use for the notation.
     *
     * @throws MalformedURLException The formal system identifier of a
     * subordinate catalog cannot be turned into a valid URL.
     * @throws IOException Error reading subordinate catalog file.
     */
    public Vector resolveAllSystem(String systemId)
	throws MalformedURLException, IOException {
	Vector resolutions = new Vector();

	// If there are SYSTEM entries in this catalog, start with them
	if (systemId != null) {
	    Vector localResolutions = resolveAllLocalSystem(systemId);
	    resolutions = appendVector(resolutions, localResolutions);
	}

	// Then look in the subordinate catalogs
	Vector subResolutions = resolveAllSubordinateCatalogs(SYSTEM,
							      null,
							      null,
							      systemId);
	resolutions = appendVector(resolutions, subResolutions);

	if (resolutions.size() > 0) {
	    return resolutions;
	} else {
	    return null;
	}
    }

    /**
     * Return all applicable SYSTEM system identifiers in this
     * catalog.
     *
     *
If one or more SYSTEM entries exists in the catalog file
     * for the system ID specified, return the mapped values.

     *
     * @param systemId The system ID to locate in the catalog
     *
     * @return A vector of the mapped system identifiers or null
     */
    private Vector resolveAllLocalSystem(String systemId) {
	Vector map = new Vector();
	String osname = System.getProperty("os.name");
	boolean windows = (osname.indexOf("Windows") >= 0);
	Enumeration enumt = catalogEntries.elements();
	while (enumt.hasMoreElements()) {
	    CatalogEntry e = (CatalogEntry) enumt.nextElement();
	    if (e.getEntryType() == SYSTEM
		&& (e.getEntryArg(0).equals(systemId)
		    || (windows
			&& e.getEntryArg(0).equalsIgnoreCase(systemId)))) {
		map.addElement(e.getEntryArg(1));
	    }
	}
	if (map.size() == 0) {
	    return null;
	} else {
	    return map;
	}
    }

    /**
     * Find the URNs for a given system identifier in the current catalog.
     *
     * @param systemId The system ID to locate.
     *
     * @return A vector of URNs that map to the systemId.
     */
    private Vector resolveLocalSystemReverse(String systemId) {
	Vector map = new Vector();
	String osname = System.getProperty("os.name");
	boolean windows = (osname.indexOf("Windows") >= 0);
	Enumeration enumt = catalogEntries.elements();
	while (enumt.hasMoreElements()) {
	    CatalogEntry e = (CatalogEntry) enumt.nextElement();
	    if (e.getEntryType() == SYSTEM
		&& (e.getEntryArg(1).equals(systemId)
		    || (windows
			&& e.getEntryArg(1).equalsIgnoreCase(systemId)))) {
		map.addElement(e.getEntryArg(0));
	    }
	}
	if (map.size() == 0) {
	    return null;
	} else {
	    return map;
	}
    }

    /**
     * Search the subordinate catalogs, in order, looking for all
     * match.
     *
     *
This method searches the Catalog and returns all of the system
     * identifiers specified for the given entity type with the given
     * name, public, and system identifiers. In some contexts, these
     * may be null.

     *
     * @param entityType The CatalogEntry type for which this query is
     * being conducted. This is necessary in order to do the approprate
     * query on a subordinate catalog.
     * @param entityName The name of the entity being searched for, if
     * appropriate.
     * @param publicId The public identifier of the entity in question
     * (as provided in the source document).
     * @param systemId The nominal system identifier for the entity
     * in question (as provided in the source document).
     *
     * @throws MalformedURLException The formal system identifier of a
     * delegated catalog cannot be turned into a valid URL.
     * @throws IOException Error reading delegated catalog file.
     *
     * @return The system identifier to use.
     * Note that the nominal system identifier is not returned if a
     * match is not found in the catalog, instead null is returned
     * to indicate that no match was found.
     */
    private synchronized Vector resolveAllSubordinateCatalogs(int entityType,
					      String entityName,
					      String publicId,
					      String systemId)
	throws MalformedURLException, IOException {

	Vector resolutions = new Vector();

	for (int catPos = 0; catPos < catalogs.size(); catPos++) {
	    Resolver c = null;

	    try {
		c = (Resolver) catalogs.elementAt(catPos);
	    } catch (ClassCastException e) {
		String catfile = (String) catalogs.elementAt(catPos);
		c = (Resolver) newCatalog();

		try {
		    c.parseCatalog(catfile);
		} catch (MalformedURLException mue) {
		    catalogManager.debug.message(1, "Malformed Catalog URL", catfile);
		} catch (FileNotFoundException fnfe) {
		    catalogManager.debug.message(1, "Failed to load catalog, file not found",
			  catfile);
		} catch (IOException ioe) {
		    catalogManager.debug.message(1, "Failed to load catalog, I/O error", catfile);
		}

		catalogs.setElementAt(c, catPos);
	    }

	    String resolved = null;

	    // Ok, now what are we supposed to call here?
	    if (entityType == DOCTYPE) {
		resolved = c.resolveDoctype(entityName,
					    publicId,
					    systemId);
		if (resolved != null) {
		    // Only find one DOCTYPE resolution
		    resolutions.addElement(resolved);
		    return resolutions;
		}
	    } else if (entityType == DOCUMENT) {
		resolved = c.resolveDocument();
		if (resolved != null) {
		    // Only find one DOCUMENT resolution
		    resolutions.addElement(resolved);
		    return resolutions;
		}
	    } else if (entityType == ENTITY) {
		resolved = c.resolveEntity(entityName,
					   publicId,
					   systemId);
		if (resolved != null) {
		    // Only find one ENTITY resolution
		    resolutions.addElement(resolved);
		    return resolutions;
		}
	    } else if (entityType == NOTATION) {
		resolved = c.resolveNotation(entityName,
					     publicId,
					     systemId);
		if (resolved != null) {
		    // Only find one NOTATION resolution
		    resolutions.addElement(resolved);
		    return resolutions;
		}
	    } else if (entityType == PUBLIC) {
		resolved = c.resolvePublic(publicId, systemId);
		if (resolved != null) {
		    // Only find one PUBLIC resolution
		    resolutions.addElement(resolved);
		    return resolutions;
		}
	    } else if (entityType == SYSTEM) {
		Vector localResolutions = c.resolveAllSystem(systemId);
		resolutions = appendVector(resolutions, localResolutions);
		break;
	    } else if (entityType == SYSTEMREVERSE) {
		Vector localResolutions = c.resolveAllSystemReverse(systemId);
		resolutions = appendVector(resolutions, localResolutions);
	    }
	}

	if (resolutions != null) {
	    return resolutions;
	} else {
	    return null;
	}
    }
}


/*
 * 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.
 */

// SAXCatalogReader.java - Read XML Catalog files

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

import java.util.Hashtable;
import java.io.IOException;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.net.MalformedURLException;
import java.net.UnknownHostException;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.parsers.SAXParser;

import org.xml.sax.AttributeList;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.DocumentHandler;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.Locator;
import org.xml.sax.Parser;
import org.xml.sax.SAXException;

import org.jboss.util.xml.catalog.Catalog;
import org.jboss.util.xml.catalog.CatalogException;
import org.jboss.util.xml.catalog.CatalogManager;
import org.jboss.util.xml.catalog.helpers.Debug;
import org.jboss.util.xml.catalog.readers.CatalogReader;

/**
 * A SAX-based CatalogReader.
 *
 *
This class is used to read XML Catalogs using the SAX. This reader
 * has an advantage over the DOM-based reader in that it functions on
 * the stream of SAX events. It has the disadvantage
 * that it cannot look around in the tree.

 *
 *
Since the choice of CatalogReaders (in the InputStream case) can only
 * be made on the basis of MIME type, the following problem occurs: only
 * one CatalogReader can exist for all XML mime types. In order to get
 * around this problem, the SAXCatalogReader relies on a set of external
 * CatalogParsers to actually build the catalog.

 *
 *
The selection of CatalogParsers is made on the basis of the QName
 * of the root element of the document.

 *
 * @see Catalog
 * @see CatalogReader
 * @see SAXCatalogReader
 * @see TextCatalogReader
 * @see DOMCatalogParser
 *
 * @author Norman Walsh
 * Norman.Walsh@Sun.COM
 *
 * @version 1.0
 */
@SuppressWarnings({"unchecked", "deprecation"})
public class SAXCatalogReader implements CatalogReader, ContentHandler, DocumentHandler {
  /** The SAX Parser Factory */
  protected SAXParserFactory parserFactory = null;

  /** The SAX Parser Class */
  protected String parserClass = null;

  /**
     * Mapping table from QNames to CatalogParser classes.
     *
     *
Each key in this hash table has the form "elementname"
     * or "{namespaceuri}elementname". The former is used if the
     * namespace URI is null.

     */
  protected Hashtable namespaceMap = new Hashtable();

  /** The parser in use for the current catalog. */
  private SAXCatalogParser saxParser = null;

  /** Set if something goes horribly wrong. It allows the class to
     * ignore the rest of the events that are received.
     */
  private boolean abandonHope = false;

  /** The Catalog that we're working for. */
  private Catalog catalog;

  /** Set the XML SAX Parser Factory.
 * @param parserFactory
   */
  public void setParserFactory(SAXParserFactory parserFactory) {
    this.parserFactory = parserFactory;
  }

  /** Set the XML SAX Parser Class
 * @param parserClass
   */
  public void setParserClass(String parserClass) {
    this.parserClass = parserClass;
  }

  /** @return the parser factory currently in use. */
  public SAXParserFactory getParserFactory() {
    return parserFactory;
  }

  /** @return the parser class currently in use. */
  public String getParserClass() {
    return parserClass;
  }

  /** The debug class to use for this reader.
   *
   * This is a bit of a hack. Anyway, whenever we read for a catalog,
   * we extract the debug object
   * from the catalog's manager so that we can use it to print messages.
   *
   * In production, we don't really expect any messages so it doesn't
   * really matter. But it's still a bit of a hack.
   */
  protected Debug debug = CatalogManager.getStaticManager().debug;

  /** The constructor */
  public SAXCatalogReader() {
    parserFactory = null;
    parserClass = null;
  }

  /** The constructor
 * @param parserFactory */
  public SAXCatalogReader(SAXParserFactory parserFactory) {
    this.parserFactory = parserFactory;
  }

  /** The constructor
 * @param parserClass */
  public SAXCatalogReader(String parserClass) {
    this.parserClass = parserClass;
  }

  /** Set the SAXCatalogParser class for the given namespace/root
     * element type.
 * @param namespaceURI
 * @param rootElement
 * @param parserClass
     */
  public void setCatalogParser(String namespaceURI,
			       String rootElement,
			       String parserClass) {
    if (namespaceURI == null) {
      namespaceMap.put(rootElement, parserClass);
    } else {
      namespaceMap.put("{"+namespaceURI+"}"+rootElement, parserClass);
    }
  }

  /** Get the SAXCatalogParser class for the given namespace/root
     * element type.
 * @param namespaceURI
 * @param rootElement
 * @return the catalog parser
     */
  public String getCatalogParser(String namespaceURI,
				 String rootElement) {
    if (namespaceURI == null) {
      return (String) namespaceMap.get(rootElement);
    } else {
      return (String) namespaceMap.get("{"+namespaceURI+"}"+rootElement);
    }
  }

  /**
   * Parse an XML Catalog file.
   *
   * @param catalog The catalog to which this catalog file belongs
   * @param fileUrl The URL or filename of the catalog file to process
   *
   * @throws MalformedURLException Improper fileUrl
   * @throws IOException Error reading catalog file
   */
  public void readCatalog(Catalog catalog, String fileUrl)
    throws MalformedURLException, IOException,
	   CatalogException {

    URL url = null;

    try {
      url = new URL(fileUrl);
    } catch (MalformedURLException e) {
      url = new URL("file:///" + fileUrl);
    }

    debug = catalog.getCatalogManager().debug;

    try {
      URLConnection urlCon = url.openConnection();
      readCatalog(catalog, urlCon.getInputStream());
    } catch (FileNotFoundException e) {
      catalog.getCatalogManager().debug.message(1, "Failed to load catalog, file not found",
		    url.toString());
    }
  }

  /**
   * Parse an XML Catalog stream.
   *
   * @param catalog The catalog to which this catalog file belongs
   * @param is The input stream from which the catalog will be read
   *
   * @throws MalformedURLException Improper fileUrl
   * @throws IOException Error reading catalog file
   * @throws CatalogException A Catalog exception
   */
  public void readCatalog(Catalog catalog, InputStream is)
    throws IOException, CatalogException {

    // Create an instance of the parser
    if (parserFactory == null && parserClass == null) {
      debug.message(1, "Cannot read SAX catalog without a parser");
      throw new CatalogException(CatalogException.UNPARSEABLE);
    }

    debug = catalog.getCatalogManager().debug;
    EntityResolver bResolver = catalog.getCatalogManager().getBootstrapResolver();

    this.catalog = catalog;

    try {
      if (parserFactory != null) {
	SAXParser parser = parserFactory.newSAXParser();
	SAXParserHandler spHandler = new SAXParserHandler();
	spHandler.setContentHandler(this);
	if (bResolver != null) {
	  spHandler.setEntityResolver(bResolver);
	}
	parser.parse(new InputSource(is), spHandler);
      } else {
	Parser parser = (Parser) Class.forName(parserClass).newInstance();
	parser.setDocumentHandler(this);
	if (bResolver != null) {
	  parser.setEntityResolver(bResolver);
	}
	parser.parse(new InputSource(is));
      }
    } catch (ClassNotFoundException cnfe) {
      throw new CatalogException(CatalogException.UNPARSEABLE);
    } catch (IllegalAccessException iae) {
      throw new CatalogException(CatalogException.UNPARSEABLE);
    } catch (InstantiationException ie) {
      throw new CatalogException(CatalogException.UNPARSEABLE);
    } catch (ParserConfigurationException pce) {
      throw new CatalogException(CatalogException.UNKNOWN_FORMAT);
    } catch (SAXException se) {
      Exception e = se.getException();
      // FIXME: there must be a better way
      UnknownHostException uhe = new UnknownHostException();
      FileNotFoundException fnfe = new FileNotFoundException();
      if (e != null) {
	if (e.getClass() == uhe.getClass()) {
	  throw new CatalogException(CatalogException.PARSE_FAILED,
				     e.toString());
	} else if (e.getClass() == fnfe.getClass()) {
	  throw new CatalogException(CatalogException.PARSE_FAILED,
				     e.toString());
	}
      }
      throw new CatalogException(se);
    }
  }

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

  /** The SAX setDocumentLocator method. Does nothing. */
  public void setDocumentLocator (Locator locator) {
    if (saxParser != null) {
      saxParser.setDocumentLocator(locator);
    }
  }

  /** The SAX startDocument method. Does nothing. */
  public void startDocument () throws SAXException {
    saxParser = null;
    abandonHope = false;
    return;
  }

  /** The SAX endDocument method. Does nothing. */
  public void endDocument ()throws SAXException {
    if (saxParser != null) {
      saxParser.endDocument();
    }
  }

  /**
   * The SAX startElement method.
   *
   *
The catalog parser is selected based on the namespace of the
   * first element encountered in the catalog.

   */
  public void startElement (String name,
			    AttributeList atts)
    throws SAXException {

    if (abandonHope) {
      return;
    }

    if (saxParser == null) {
      String prefix = "";
      if (name.indexOf(':') > 0) {
	prefix = name.substring(0, name.indexOf(':'));
      }

      String localName = name;
      if (localName.indexOf(':') > 0) {
	localName = localName.substring(localName.indexOf(':')+1);
      }

      String namespaceURI = null;
      if (prefix.equals("")) {
	namespaceURI = atts.getValue("xmlns");
      } else {
	namespaceURI = atts.getValue("xmlns:" + prefix);
      }

      String saxParserClass = getCatalogParser(namespaceURI,
					       localName);

      if (saxParserClass == null) {
	abandonHope = true;
	if (namespaceURI == null) {
	  debug.message(2, "No Catalog parser for " + name);
	} else {
	  debug.message(2, "No Catalog parser for "
			+ "{" + namespaceURI + "}"
			+ name);
	}
	return;
      }

      try {
	saxParser = (SAXCatalogParser)
	  Class.forName(saxParserClass).newInstance();

	saxParser.setCatalog(catalog);
	saxParser.startDocument();
	saxParser.startElement(name, atts);
      } catch (ClassNotFoundException cnfe) {
	saxParser = null;
	abandonHope = true;
	debug.message(2, cnfe.toString());
      } catch (InstantiationException ie) {
	saxParser = null;
	abandonHope = true;
	debug.message(2, ie.toString());
      } catch (IllegalAccessException iae) {
	saxParser = null;
	abandonHope = true;
	debug.message(2, iae.toString());
      } catch (ClassCastException cce ) {
	saxParser = null;
	abandonHope = true;
	debug.message(2, cce.toString());
      }
    } else {
      saxParser.startElement(name, atts);
    }
  }

  /**
   * The SAX2 startElement method.
   *
   *
The catalog parser is selected based on the namespace of the
   * first element encountered in the catalog.

   */
  public void startElement (String namespaceURI,
			    String localName,
			    String qName,
			    Attributes atts)
    throws SAXException {

    if (abandonHope) {
      return;
    }

    if (saxParser == null) {
      String saxParserClass = getCatalogParser(namespaceURI,
					       localName);

      if (saxParserClass == null) {
	abandonHope = true;
	if (namespaceURI == null) {
	  debug.message(2, "No Catalog parser for " + localName);
	} else {
	  debug.message(2, "No Catalog parser for "
			+ "{" + namespaceURI + "}"
			+ localName);
	}
	return;
      }

      try {
	saxParser = (SAXCatalogParser)
	  Class.forName(saxParserClass).newInstance();

	saxParser.setCatalog(catalog);
	saxParser.startDocument();
	saxParser.startElement(namespaceURI, localName, qName, atts);
      } catch (ClassNotFoundException cnfe) {
	saxParser = null;
	abandonHope = true;
	debug.message(2, cnfe.toString());
      } catch (InstantiationException ie) {
	saxParser = null;
	abandonHope = true;
	debug.message(2, ie.toString());
      } catch (IllegalAccessException iae) {
	saxParser = null;
	abandonHope = true;
	debug.message(2, iae.toString());
      } catch (ClassCastException cce ) {
	saxParser = null;
	abandonHope = true;
	debug.message(2, cce.toString());
      }
    } else {
      saxParser.startElement(namespaceURI, localName, qName, atts);
    }
  }

  /** The SAX endElement method. Does nothing. */
  public void endElement (String name) throws SAXException {
    if (saxParser != null) {
      saxParser.endElement(name);
    }
  }

  /** The SAX2 endElement method. Does nothing. */
  public void endElement (String namespaceURI,
			  String localName,
			  String qName) throws SAXException {
    if (saxParser != null) {
      saxParser.endElement(namespaceURI, localName, qName);
    }
  }

  /** The SAX characters method. Does nothing. */
  public void characters (char ch[], int start, int length)
    throws SAXException {
    if (saxParser != null) {
      saxParser.characters(ch, start, length);
    }
  }

  /** The SAX ignorableWhitespace method. Does nothing. */
  public void ignorableWhitespace (char ch[], int start, int length)
    throws SAXException {
    if (saxParser != null) {
      saxParser.ignorableWhitespace(ch, start, length);
    }
  }

  /** The SAX processingInstruction method. Does nothing. */
  public void processingInstruction (String target, String data)
    throws SAXException {
    if (saxParser != null) {
      saxParser.processingInstruction(target, data);
    }
  }

  /** The SAX startPrefixMapping method. Does nothing. */
  public void startPrefixMapping (String prefix, String uri)
    throws SAXException {
    if (saxParser != null) {
      saxParser.startPrefixMapping (prefix, uri);
    }
  }

  /** The SAX endPrefixMapping method. Does nothing. */
  public void endPrefixMapping (String prefix)
    throws SAXException {
    if (saxParser != null) {
      saxParser.endPrefixMapping (prefix);
    }
  }

  /** The SAX skippedentity method. Does nothing. */
  public void skippedEntity (String name)
    throws SAXException {
    if (saxParser != null) {
      saxParser.skippedEntity(name);
    }
  }
}


/*
 * 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.
 */

// SAXParserHandler.java - An entity-resolving DefaultHandler

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

import java.io.IOException;

import org.xml.sax.*;
import org.xml.sax.helpers.*;

/**
 * An entity-resolving DefaultHandler.
 *
 *
This class provides a SAXParser DefaultHandler that performs
 * entity resolution.
 *

 *
 * @author Norman Walsh
 * Norman.Walsh@Sun.COM
 * @version 1.0
 */
public class SAXParserHandler extends DefaultHandler {
  private EntityResolver er = null;
  private ContentHandler ch = null;

  public SAXParserHandler() {
    super();
  }

  public void setEntityResolver(EntityResolver er) {
    this.er = er;
  }

  public void setContentHandler(ContentHandler ch) {
    this.ch = ch;
  }

  // Entity Resolver
  public InputSource resolveEntity(String publicId, String systemId)
    throws SAXException {

    if (er != null) {
      try {
	return er.resolveEntity(publicId, systemId);
      }	catch (IOException e) {
	  System.out.println("resolveEntity threw IOException!");
	  return null;
      }
    } else {
      return null;
    }
  }

  // Content Handler
  public void characters(char[] ch, int start, int length)
    throws SAXException {
    if (this.ch != null) {
      this.ch.characters(ch, start, length);
    }
  }

  public void endDocument()
    throws SAXException {
    if (ch != null) {
      ch.endDocument();
    }
  }

  public void endElement(String namespaceURI, String localName, String qName)
    throws SAXException {
    if (ch != null) {
      ch.endElement(namespaceURI, localName, qName);
    }
  }

  public void endPrefixMapping(String prefix)
    throws SAXException {
    if (ch != null) {
      ch.endPrefixMapping(prefix);
    }
  }

  public void ignorableWhitespace(char[] ch, int start, int length)
    throws SAXException {
    if (this.ch != null) {
      this.ch.ignorableWhitespace(ch, start, length);
    }
  }

  public void processingInstruction(String target, String data)
    throws SAXException {
    if (ch != null) {
      ch.processingInstruction(target, data);
    }
  }

  public void setDocumentLocator(Locator locator) {
    if (ch != null) {
      ch.setDocumentLocator(locator);
    }
  }

  public void skippedEntity(String name)
    throws SAXException {
    if (ch != null) {
      ch.skippedEntity(name);
    }
  }

  public void startDocument()
    throws SAXException {
    if (ch != null) {
      ch.startDocument();
    }
  }

  public void startElement(String namespaceURI, String localName,
			   String qName, Attributes atts)
    throws SAXException {
    if (ch != null) {
      ch.startElement(namespaceURI, localName, qName, atts);
    }
  }

  public void startPrefixMapping(String prefix, String uri)
    throws SAXException {
    if (ch != null) {
      ch.startPrefixMapping(prefix, uri);
    }
  }
}


/*
 * 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.Iterator;
import java.util.Map;
import java.util.HashMap;
import java.util.LinkedList;
import java.io.StringWriter;
import java.io.PrintWriter;

/**
 * Semaphore that can allow a specified number of threads to enter, blocking the
 * others. If the specified number of threads is 1, it acts as an exclusive semaphore
 * and can be used instead of synchronized blocks
 *
 * @author Simone Bordet
 * @version $Revision$
 */
@SuppressWarnings("unchecked")
public class Semaphore
   implements Sync
{
   // Constants -----------------------------------------------------
   private static final long DEADLOCK_TIMEOUT = 5*60*1000;

   // Attributes ----------------------------------------------------
   private final static boolean m_debug = false;
   private int m_users;
   private int m_allowed;
   private Map m_logMap;

   // Static --------------------------------------------------------

   // Constructors --------------------------------------------------
   public Semaphore(int allowed)
   {
      if (allowed < 1) throw new IllegalArgumentException();

      m_users = 0;
      m_allowed = allowed;
      m_logMap = new HashMap();
   }

   // Public --------------------------------------------------------
   public int getUsers()
   {
      synchronized (this)
      {
         return m_users;
      }
   }

   // Sync implementation ----------------------------------------------
   public void acquire() throws InterruptedException
   {
      synchronized (this)
      {
         logAcquire();

         // One user more called acquire, increase users
         ++m_users;
         boolean waitSuccessful = false;
         while (m_allowed <= 0)
         {
            waitSuccessful = waitImpl(this);
            if (!waitSuccessful)
            {
               // Dealock was detected, restore status, 'cause it's like a release()
               // that will probably be never called
               --m_users;
               ++m_allowed;
            }
         }
         --m_allowed;
      }
   }

   public void release()
   {
      synchronized (this)
      {
         logRelease();

         --m_users;
         ++m_allowed;
         notify();
      }
   }

   // Object overrides ---------------------------------------------------
   public String toString()
   {
      return super.toString() + " - " + m_users;
   }

   // Package protected ---------------------------------------------

   // Protected -----------------------------------------------------
   protected boolean waitImpl(Object lock) throws InterruptedException
   {
      // Wait (forever) until notified. To discover deadlocks,
      // turn on debugging of this class
      long start = System.currentTimeMillis();
      lock.wait(DEADLOCK_TIMEOUT);
      long end = System.currentTimeMillis();

      if ((end - start) > (DEADLOCK_TIMEOUT - 1000))
      {
         logDeadlock();
         return false;
      }
      return true;
   }

   protected void logAcquire()
   {
      if (m_debug)
      {
         // Check if thread is already mapped
         Thread thread = Thread.currentThread();

         // Create stack trace
         StringWriter sw = new StringWriter();
         new Exception().printStackTrace(new PrintWriter(sw));
         String trace = sw.toString();

         LinkedList list = (LinkedList)m_logMap.get(thread);
         if (list != null)
         {
            // Thread is mapped
            // Add info
            Info prevInfo = (Info)list.getLast();
            Info info = new Info(thread, m_users, trace);
            list.add(info);
         }
         else
         {
            // Thread is not mapped, create list and add counter
            list = new LinkedList();
            Info info = new Info(thread, m_users, trace);
            list.add(info);
            // Map thread
            m_logMap.put(thread, list);
         }
      }
   }
   protected void logDeadlock()
   {
      System.err.println();
      System.err.println("DEADLOCK ON SEMAPHORE " + this);
      if (m_debug)
      {
         for (Iterator i = m_logMap.values().iterator(); i.hasNext();)
         {
            LinkedList list = (LinkedList)i.next();
            for (Iterator iter = list.iterator(); iter.hasNext();)
            {
               System.err.println(iter.next());
            }
         }
      }
      System.err.println();
   }
   protected void logRelease()
   {
      if (m_debug)
      {
         // Find a matching thread and remove info for it
         Thread thread = Thread.currentThread();

         LinkedList list = (LinkedList)m_logMap.get(thread);
         if (list != null)
         {
            Info info = new Info(thread, 0, "");
            if (!list.remove(info))
            {
               System.err.println("LOG INFO SIZE: " + list);
               new IllegalStateException("BUG: semaphore log list does not contain required info").printStackTrace();
            }

            // If no info left, remove the mapping
            int size = list.size();
            if (size < 1)
            {
               m_logMap.remove(thread);
            }
         }
         else
         {
            throw new IllegalStateException("Semaphore log failed: release called without acquire");
         }
      }
   }

   // Private -------------------------------------------------------

   // Inner classes -------------------------------------------------
   private class Info
   {
      private Info(Thread t, int i, String s)
      {
         m_thread = t;
         m_counter = i;
         m_trace = s;
      }
      private Thread m_thread;
      private int m_counter;
      private String m_trace;
      public boolean equals(Object o)
      {
         Info other = (Info)o;
         return m_thread == other.m_thread;
      }
      public String toString()
      {
         return m_thread + " - " + m_counter + "\n" + m_trace;
      }
   }
}


/*
 * 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.file;

import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FilePermission;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.*;
import java.security.Permission;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;

/**
 * Provides local file access via URL semantics, correctly returning
 * the last modified time of the underlying file.
 *
 * @author  Jason Dillon
 * @author  Scott Stark
 * @author  Dimitris Andreadis
 * @author  Ales Justin
 * @version $Revision$
 */
public class FileURLConnection extends URLConnection
{
   static boolean decodeFilePaths = true;

   // JBPAPP-8065 - regression introduced where fixing JBCOMMON-106 exposed bugs in some third party frameworks,
   //  setting this property false enables the previous behavior to allow it to continue to work
   static boolean useURI = true;

   static
   {
      String flag = System.getProperty("org.jboss.net.protocol.file.decodeFilePaths");
      if (flag != null)
      {
         decodeFilePaths = Boolean.valueOf(flag).booleanValue();
      }

      flag = System.getProperty("org.jboss.net.protocol.file.useURI");
      if (flag != null)
      {
         useURI = Boolean.valueOf(flag).booleanValue();
      }
   }

   /** The underlying file */
   protected final File file;

   public FileURLConnection(final URL url) throws IOException
   {
      super(url);
      try
      {
         if (useURI)
         {
            file = new File(url.toURI());
         }
         else
         {
            String path = url.getPath();
            if (decodeFilePaths)
            {
               path = URLDecoder.decode(path, "UTF-8");
            }

            // Convert the url '/' to the os file separator
            file = new File(path.replace('/', File.separatorChar).replace('|', ':'));
         }
         super.doOutput = false;
      }
      catch (URISyntaxException e)
      {
         IOException ioe = new IOException();
         ioe.initCause(e);
         throw ioe;
      }
   }

   /**
    * Returns the underlying file for this connection.
    * @return the file
    */
   public File getFile()
   {
      return file;
   }

   /**
    * Checks if the underlying file for this connection exists.
    *
    * @throws FileNotFoundException
    */
   public void connect() throws IOException
   {
      if (connected)
         return;

      if (!file.exists())
      {
         throw new FileNotFoundException(file.getPath());
      }
      connected = true;
   }

   public InputStream getInputStream() throws IOException
   {
      connect();

      if (file.isDirectory())
      {
         // return a sorted list of the directory contents
         String[] files = file.list();
         Arrays.sort(files);
         StringBuilder sb = new StringBuilder();
         for (int i = 0; i < files.length; i++)
         {
            sb.append(files[i]).append("\n");
         }
         return new ByteArrayInputStream(sb.toString().getBytes());
      }
      else
      {
         return new FileInputStream(file);
      }
   }

   // We should probably disallow this?
   public OutputStream getOutputStream() throws IOException
   {
      connect();

      SecurityManager sm = System.getSecurityManager();
      if (sm != null)
      {
         // Check for write access
         FilePermission p = new FilePermission(file.getPath(), "write");
         sm.checkPermission(p);
      }
      return new FileOutputStream(file);
   }

   /**
    * Provides support for the following headers:
    *
    * <tt>last-modified</tt>
    * <tt>content-length</tt>
    * <tt>content-type</tt>
    * <tt>date</tt>
    */
   public String getHeaderField(final String name)
   {
      String headerField = null;
      if (name.equalsIgnoreCase("last-modified"))
      {
         long lastModified = getLastModified();
         if (lastModified != 0)
         {
            // return the last modified date formatted according to RFC 1123
            Date modifiedDate = new Date(lastModified);
            SimpleDateFormat sdf = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss 'GMT'", Locale.US);
            sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
            headerField = sdf.format(modifiedDate);
         }
      }
      else if (name.equalsIgnoreCase("content-length"))
      {
         headerField = String.valueOf(file.length());
      }
      else if (name.equalsIgnoreCase("content-type"))
      {
         if (file.isDirectory())
         {
            headerField = "text/plain";
         }
         else
         {
            headerField = getFileNameMap().getContentTypeFor(file.getName());
            if (headerField == null)
            {
               try
               {
                  InputStream is = getInputStream();
                  BufferedInputStream bis = new BufferedInputStream(is);
                  headerField = URLConnection.guessContentTypeFromStream(bis);
                  bis.close();
               }
               catch(IOException e)
               {
                  // ignore
               }
            }
         }
      }
      else if (name.equalsIgnoreCase("date"))
      {
         headerField = String.valueOf(getLastModified());
      }
      else
      {
         // This always returns null currently
         headerField = super.getHeaderField(name);
      }
      return headerField;
   }

   /**
    * Return a permission for reading of the file
    */
   public Permission getPermission() throws IOException
   {
      return new FilePermission(file.getPath(), "read");
   }

   /**
    * Returns the last modified time of the underlying file.
    */
   public long getLastModified()
   {
      return file.lastModified();
   }
}