// Sample code file: ZipNamespace.java

// Warning: This code has been marked up for HTML

/*
   Copyright (c) 1997-1999 Novell, Inc.  All Rights Reserved.

   THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND TREATIES.
   USE AND REDISTRIBUTION OF THIS WORK IS SUBJECT TO THE LICENSE AGREEMENT
   ACCOMPANYING THE SOFTWARE DEVELOPMENT KIT (SDK) THAT CONTAINS THIS WORK.
   PURSUANT TO THE SDK LICENSE AGREEMENT, NOVELL HEREBY GRANTS TO DEVELOPER A
   ROYALTY-FREE, NON-EXCLUSIVE LICENSE TO INCLUDE NOVELL'S SAMPLE CODE IN ITS
   PRODUCT. NOVELL GRANTS DEVELOPER WORLDWIDE DISTRIBUTION RIGHTS TO MARKET,
   DISTRIBUTE, OR SELL NOVELL'S SAMPLE CODE AS A COMPONENT OF DEVELOPER'S
   PRODUCTS. NOVELL SHALL HAVE NO OBLIGATIONS TO DEVELOPER OR DEVELOPER'S
   CUSTOMERS WITH RESPECT TO THIS CODE.
*/

package com.novell.sample.snapins.filesystem;

import java.util.*;
import java.awt.*;
import java.net.*;
import java.io.*;
import java.util.zip.*;
import javax.swing.*;

// ConsoleOne snapin interfaces.
import com.novell.application.console.snapin.*;
import com.novell.application.console.snapin.scope.*;
import com.novell.application.console.snapin.context.*;

/**
  This is an example of a namespace snap-in registered also as a zip
  file federation.
*/
public class ZipNamespace
   implements NamespaceSnapin, MapObjectEntrySnapin
{
   /** Shell instance */
   private Shell shell = null;

   /**
     Globally unique ID that identifies this namespace. It is used by
     other snapins that want to register against this namespace.
   */
   private static final String NAMESPACE_UNIQUE_ID = "Novell Sample Zip File System";

   private static Hashtable store = null;
   private static ObjectType dirType = null;
   private static ObjectType leafType = null;

   /**
    The getSnapinName() method (derived from Snapin interface) is called by
    configurators to display the localized name of this snap-in. It returns
    the translated name for this snap-in. The shell displays the name you
    provide together with all the snap-ins of a given type. The user is then
    allowed to select which snap-ins should be set as active in the current
    configuration. The getSnapinName() method may be called before initSnapin().
    */
   public String getSnapinName()
   {
      return NAMESPACE_UNIQUE_ID;
   }

   /**
    The getSnapinDescription() method (derived from Snapin interface) is called
    by the configurators to display the localized description information
    about this snap-in. It returns the snap-in description as a String.
    */
   public String getSnapinDescription()
   {
      return "Novell's Sample Filesystem Zip file namespace";
   }

   /**
    The initSnapin() method initializes this class. You should do any
    onetime initialization here, such as adding event listeners. The method
    returns a boolean set to <i>true</i> if the snap-in is able to successfully
    complete initialization, or <i>false</i> if initialization fails. The 'info'
    parameter contains data the snap-in may use for initialization, such as
    references to the shell and the snap-in type, and may contain a reference
    to snap-in context data.
    */
   public boolean initSnapin(InitSnapinInfo info)
   {
      shell = info.getShell();
      if(store == null)
      {
         store = new Hashtable();
         dirType = new ObjectType("ZipDirectory", this, true);
         leafType = new ObjectType("ZipLeaf", this, false);
      }
      return true;
   }

   /**
    Implement shutdownSnapin() in your extending snap-in if there is
    anything that needs to be cleaned up, such as removing shell event listeners.
    */
   public void shutdownSnapin()
   {
   }

   /**
    The getUniqueID() method is used to get the globally unique identifier or
    name of the namespace. You must return a name that the shell will use for
    the namespace being described. The unique namespace identifier is the
    key used to register snap-ins that will operate against this namespace.
    It is suggested that you return, as the uniqueID string, the package
    name and class implemented in the namespace snap-in.
    */
   public String getUniqueID()
   {
     return NAMESPACE_UNIQUE_ID;
   }

   /**
     The getFullName() method is used to return the full name of the passed
     in ObjectEntry for which the full name is desired. The full name should
     represent a unique name in the namespace.
    */
   public String getFullName(ObjectEntry entry)
   {
      return entry.getName();
   }

   /**
     The getObjectEntry() method is used to return the ObjectEntry associated
     with a unique string, 'name'. If no ObjectEntry is found an exception is
     thrown.
    */
   public ObjectEntry getObjectEntry(String s) throws ObjectNotFoundException
   {
      // This is not complete.  It should look up the entry from the given
      // name and return it.
      return null;
   }

   /**
     The getInitialObjectEntries() method is used to return the root ObjectEntry
     array. Null can be returned if there are no root object entries in the
     namespace.
    */
   public ObjectEntry[] getInitialObjectEntries()
   {
      return null;
   }

   /**
   * fills the local hashtable with all zip entries
   */
   private void addZipToStore(ObjectEntry file)
   {
      String zipFile = file.getName();
      if (store == null)
      {
         System.err.println(" Zip Store is null");
         store = new Hashtable();
         dirType = new ObjectType("ZipDirectory", this, true);
         leafType = new ObjectType("ZipLeaf", this, false);
      }
      try
      {
         ZipFile zip = new ZipFile(zipFile);
         Vector rootVector = null;
         if (!store.containsKey(zipFile))
         {
            store.put(zipFile, rootVector = new Vector());
         }

         Enumeration entries = zip.entries();
         while (entries.hasMoreElements())
         {
            ZipEntry ze = (ZipEntry) entries.nextElement();
            if (!ze.isDirectory())
            {
               addPath(ze.getName(), file);
            }
         }
      }
      catch (IOException e)
      {
         System.out.println("Error adding zip file : " + e.toString());
      }
   }

   private void addPath(String path, ObjectEntry parent)
   {
      int startFrom = 0;
      int slash = 0;
      ObjectEntry currParent = parent;
      ObjectEntry oe = null;
      while (startFrom > -1)
      {
         slash = path.indexOf("/", startFrom);
         if (slash == -1)
         {
            slash = path.indexOf("\\", startFrom);
         }
         String dir = null;
         Vector parentList = null;
         if (store.containsKey(currParent.getName()))
         {
            parentList = (Vector) store.get(currParent.getName());
         }
         else
         {
            parentList = new Vector();
            store.put(currParent.getName(), parentList);
         }

         if (slash > -1)
         {
            startFrom = slash + 1;
            dir = path.substring(0, slash);
            boolean found = false;
            for(int i=0; i<parentList.size(); i++)
            {
               ObjectEntry oe2 = (ObjectEntry) parentList.elementAt(i);
               if(oe2.getName().equals(dir))
               {
                  oe = oe2;
                  found = true;
               }
            }
            if(!found)
            {
               oe = new ObjectEntry(dir, dirType, currParent);
               parentList.addElement(oe);
            }
            currParent = oe;
         }
         else
         {
            startFrom = slash;
            dir = path;
            oe = new ObjectEntry(dir, leafType, currParent);
            parentList.addElement(oe);
         }
      }
   }

   /**
   * Maps an ObjectEntry in one namespace to a ObjectEntry in another namespace.
   *
   * @param entry The source ObjectEntry to correlate in another namespace.
   *
   * @return ObjectEntry from the target namespace that corresponds to the
   *         ObjectEntry in the source namespace.
   */
   public ObjectEntry mapObjectEntry(ObjectEntry parent)
   {
      addZipToStore(parent);
      return new ObjectEntry(parent.getName(),new ObjectType("ZipDirectory", this, true));
   }

   /**
     The getChildren() method returns the enumeration of children for the
     'parent' ObjectEntry. This should not be called with anything besides
     a container parent.
    */
   public ObjectEntryEnumeration getChildren(ObjectEntry parent) throws NotAContainerException
   {
      //System.out.println("ZipFileNamespace::getChildren.parent type= "+parent.getObjectType().getName());
      if (parent.getObjectType().getName().equals("ZipDirectory"))
      {
         // handle federation from file system zip files
         //System.out.println(" zip getChildren() of " + parent.getName());
         Vector v = (Vector)store.get(parent.getName());
         return new ZipObjectEnumeration(v);
      }
      else
      {
         throw new NotAContainerException();
      }
      /*
      else
      {
         if (store.containsKey(parent.getName()))
         {
            return new ZipObjectEnumeration(
               (Vector) store.get(parent.getName()));
         }
         else
         {
            System.out.println("Store does not contain key"
               + parent.getName());
            return null;
         }
      }
      */
   }

   public ObjectEntryEnumeration getChildren(ObjectEntry parent, ResultModifier modifier) throws NotAContainerException
   {
      return new ResultModifiedEnumeration(getChildren(parent), modifier);
   }

   public ObjectEntryEnumeration getChildContainers(ObjectEntry parent, ResultModifier modifier) throws NotAContainerException
   {
      return new DefaultChildContainersObjectEntryEnumeration(getChildren(parent));
   }

   public ObjectType[] getObjectTypes(ObjectEntry referenceContext)
   {
      ObjectType[] types = new ObjectType[2];
      types[0] = dirType;
      types[1] = leafType;
      return types;
   }
}

class ZipObjectEnumeration implements ObjectEntryEnumeration
{
   private Enumeration enum = null;

   public ZipObjectEnumeration(Vector list)
   {
      enum = list.elements();
   }

   /**
     The hasMoreElements() method returns a boolean determining if there
     are more children in the enumeration. Return <i>true</i> if there
     are more children in the Enumeration.
   */
   public boolean hasMoreElements()
   {
     return enum.hasMoreElements();
   }

   /**
     Returns the next child as an Object. (Must still be an ObjectEntry)
   */
   public Object nextElement()
   {
      return next();
   }

   /**
     Return the next child in the model as an ObjectEntry.
   */
   public ObjectEntry next()
   {
      return (ObjectEntry) enum.nextElement();
   }
}