/**
 * Copyright (c) 2004-2006 IBM Corporation and others.
 * All rights reserved.   This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v2.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v20.html
 * 
 * Contributors: 
 *   IBM - Initial API and implementation
 */
package org.eclipse.emf.common.archive;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLStreamHandler;

/**
 * A URL stream handler that can be {@link #register() registered} to support archive access protocol.
 * It uses {@link ArchiveURLConnection} to implement the connection.
 */
public class Handler extends URLStreamHandler
{
  /**
   * Registers this class.
   * A handler for protocol "xyz" is registered 
   * by providing a class named Handler implementing {@link URLStreamHandler}
   * in a package called named xyz in a package of your choosing,
   * and then registering that chosen prefix package name 
   * in the system property for <code>"java.protocol.handler.pkgs"</code>,
   * which is an "|" separated list of package prefixes to search for handlers.
   */
  public static void register()
  {
    String javaProtocolHandlerPkgs = System.getProperty("java.protocol.handler.pkgs");
    if (javaProtocolHandlerPkgs == null || javaProtocolHandlerPkgs.length() == 0)
    {
      javaProtocolHandlerPkgs = "org.eclipse.emf.common";
    }
    else
    {
      javaProtocolHandlerPkgs += "|org.eclipse.emf.common";
    }
    System.setProperty("java.protocol.handler.pkgs", javaProtocolHandlerPkgs); 
  }
  
  /**
   * Registers this handler and interprets each argument as URL to be opened, read in, and written out to System.out.
   * @param args URLs to open, read, and write to System.out
   * @throws IOException if there are problems opening or reading from the URLs, or writing to System.out.
   */
  public static void main(String[] args) throws IOException
  {
    register();
    
    for (int i = 0; i < args.length; ++i)
    {
      InputStream inputStream = new URL(args[i]).openStream();
      byte [] bytes = new byte [4048];
      for (int size; (size = inputStream.read(bytes, 0, bytes.length)) > -1; )
      {
        System.out.write(bytes, 0, size);
      }
    }
  }
  
  /**
   *  Creates an instance.
   */
  public Handler()
  {
    super();
  }
  
  /**
   * Overrides parsing the URL to validate constraints on well formed archive syntax.
   * @see URLStreamHandler#parseURL(java.net.URL, java.lang.String, int, int)
   */
  @Override
  protected void parseURL(URL url, String specification, int start, int limit)
  {
    super.parseURL(url, specification, start, limit);
      
    // There needs to be another URL protocol right after the archive protocol, and not a "/".
    //
    if (start > limit || specification.charAt(start) == '/')
    {
      throw 
        new IllegalArgumentException
          ("archive protocol must be immediately followed by another URL protocol " + specification);
    }
    
    // There must be at least one archive path.
    //
    int archiveSeparator = specification.indexOf("!/", start);
    if (archiveSeparator < 0)
    {
      throw new IllegalArgumentException("missing archive separators " + specification.substring(start, limit));
    }
          
    // Parse to count the archive paths that must will be delegated to the nested URL based on the number of schemes at the start.
    //
    for (int i = start, end = specification.indexOf('/', start) - 1; (i = specification.indexOf(':', i)) < end; ++i)
    {
      // There should be at least one archive separator per scheme.
      //
      archiveSeparator = specification.indexOf("!/", archiveSeparator + 2);
      if (archiveSeparator < 0)
      {
        throw new IllegalArgumentException("too few archive separators " + specification);
      }
    }
  }
  
  /**
   *  Returns a new {@link ArchiveURLConnection}.
   */
  @Override
  protected URLConnection openConnection(URL url) throws IOException
  {
    return new ArchiveURLConnection(url);
  }
}
