/*
 * @(#)HierarchicCommandMap.java  1.1  2000-09-30
 *
 * Copyright (c) 1999 Werner Randelshofer, Immensee, Switzerland.
 * All rights reserved.
 *
 * You may not use, copy or modify this file, except in compliance with the
 * license agreement you entered into with Werner Randelshofer.
 * For details see accompanying license terms.
 */
package ch.randelshofer.activation;

import javax.activation.CommandMap;
import javax.activation.CommandInfo;
import javax.activation.DataContentHandler;
import java.util.Vector;
import java.util.Enumeration;

/**
 * This command map allows the specification of generic commands,
 * that can be applied to multiple mime types.
 * e.g When commands are queried for the file type "audio/basic", this
 * map returns all the commands for the file types "audio/basic", "audio/*"
 * and "* /*".
 *
 * @author  Werner Randelshofer, Hausmatt 10, CH-6405 Immensee, Switzerland.
 * @version 1.1  2000-09-30 Method #removeDuplicates is more restrict when
 *                          removing duplicates.
 * <br> 1.0  1999-10-19
 */
public class HierarchicCommandMap
extends BasicCommandMap {
    private final static String GENERIC_TYPE = "*/*";
    
    /**
     * Get the preferred command list from a MIME Type.
     *
     * e.g When commands are queried for the file type "audio/basic", this
     * map returns all the commands for the file types "audio/basic", "audio/*"
     * and "* /*".
     *
     * @return the CommandInfo classes the represent the command Beans.
     */
    public CommandInfo[] getPreferredCommands(String mimeType) {
        Vector v = new Vector();
        
        while (true) {
            CommandInfo[] infos = super.getPreferredCommands(mimeType);
            for (int i=0; i < infos.length; i++)
            { v.addElement( infos[i] ); }
            if ( mimeType.equals(GENERIC_TYPE) )
            { break; }
            mimeType = generalize(mimeType);
        }
        
        removeDuplicates(v);
        CommandInfo[] result = new CommandInfo[v.size()];
        v.copyInto(result);
        return result;
    }
    
    /**
     * Returns a more general version of a mime Type.
     *
     * <p>e.g.
     * <br>"audio/basic" returns "audio/*".
     * <br>"audio/*" returns "* /*".
     * <br>"* /*" returns "* /*".
     */
    private final static String generalize(String mimeType) {
        if (mimeType.equals(GENERIC_TYPE) )
        { return GENERIC_TYPE; }
        if (mimeType.endsWith("/*"))
        { mimeType = mimeType.substring(0,mimeType.length()-2); }
        int slash = mimeType.lastIndexOf('/');
        if (slash == -1)
        { return GENERIC_TYPE; }
        else
        { return mimeType.substring(0,slash+1)+"*"; }
    }
    /**
     * Get all the available commands for this type.
     * This method should return all the possible commands for this
     * MIME type.
     *
     * @return returns the CommandInfo objects representing all the commands.
     */
    public CommandInfo[] getAllCommands(String mimeType) {
        Vector v = new Vector();
        
        while (true) {
            CommandInfo[] infos = super.getAllCommands(mimeType);
            for (int i=0; i < infos.length; i++) {
                v.addElement( infos[i] );
            }
            if ( mimeType.equals(GENERIC_TYPE) ) {
                break;
            }
            mimeType = generalize(mimeType);
        }
        removeDuplicates(v);
        CommandInfo[] result = new CommandInfo[v.size()];
        v.copyInto(result);
        return result;
    }
    
    /**
     * Removes duplicate entries in the vector.
     * The vector must contain instances of CommandInfo only.
     */
    private final static void removeDuplicates(Vector v) {
        int i = 0;
        int j;
        while (i < v.size()) {
            CommandInfo a = (CommandInfo)v.elementAt(i);
            for (j=0; j < i; j++) {
                CommandInfo b = (CommandInfo)v.elementAt(j);
                if (a.getCommandName().equals(b.getCommandName())
                || a.getCommandClass().equals(b.getCommandClass())) {
                    break;
                }
            }
            if (j < i) {
                v.removeElementAt(i);
            } else {
                i++;
            }
        }
    }
    
    /**
     * Get the default command corresponding to
     * the MIME type.
     *
     * @return the CommandInfo corresponding to the command.
     */
    public CommandInfo getCommand(String mimeType, String cmdName) {
        mimeType = mimeType.intern();
        CommandInfo result = super.getCommand(mimeType,cmdName);
        while (result == null && mimeType != GENERIC_TYPE) {
            mimeType = generalize(mimeType);
            result = super.getCommand(mimeType,cmdName);
        }
        return result;
    }
    
    /**
     * Locate a DataContentHandler that corresponds to the MIME type.
     * The mechanism and semantics for determining this are determined by
     * the implementation of the particular CommandMap.
     */
    public DataContentHandler createDataContentHandler(String mimeType) {
        mimeType = mimeType.intern();
        DataContentHandler result = super.createDataContentHandler(mimeType);
        while (result == null && mimeType != GENERIC_TYPE) {
            mimeType = generalize(mimeType);
            result = super.createDataContentHandler(mimeType);
        }
        return result;
    }
}
