/*****************************************************************************
 * Copyright (C) The Apache Software Foundation. All rights reserved.        *
 * ------------------------------------------------------------------------- *
 * This software is published under the terms of the Apache Software License *
 * version 1.1, a copy of which has been included with this distribution in  *
 * the LICENSE file.                                                         *
 *****************************************************************************/

package org.apache.batik.apps.svgbrowser;

import java.awt.Dimension;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.Writer;
import java.net.Authenticator;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.StringTokenizer;
import java.util.Vector;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ImageIcon;
import javax.swing.JOptionPane;
import javax.swing.JProgressBar;
import javax.swing.UIManager;
import javax.swing.plaf.FontUIResource;

import org.apache.batik.swing.JSVGCanvas;
import org.apache.batik.swing.gvt.GVTTreeRendererAdapter;
import org.apache.batik.swing.gvt.GVTTreeRendererEvent;
import org.apache.batik.swing.svg.GVTTreeBuilderAdapter;
import org.apache.batik.swing.svg.GVTTreeBuilderEvent;
import org.apache.batik.swing.svg.SVGDocumentLoaderAdapter;
import org.apache.batik.swing.svg.SVGDocumentLoaderEvent;
import org.apache.batik.util.ApplicationSecurityEnforcer;
import org.apache.batik.util.ParsedURL;
import org.apache.batik.util.SVGConstants;
import org.apache.batik.util.XMLResourceDescriptor;
import org.apache.batik.util.gui.resource.ResourceManager;

/**
 * This class contains the main method of an SVG viewer.
 *
 * @author <a href="mailto:stephane@hillion.org">Stephane Hillion</a>
 * @version $Id: Main.java,v 1.47 2003/04/11 13:54:38 vhardy Exp $
 */
public class Main implements Application {
    /**
     * Extension used in addition to the scriptType value
     * to read from the PreferenceManager whether or not the
     * scriptType can be loaded.
     */
    public static final String UNKNOWN_SCRIPT_TYPE_LOAD_KEY_EXTENSION 
        = ".load";

    /**
     * User home property
     */
    public static final String PROPERTY_USER_HOME = "user.home";

    /**
     * System property for specifying an additional policy file.
     */
    public static final String PROPERTY_JAVA_SECURITY_POLICY 
        = "java.security.policy";

    /**
     * Batik configuration sub-directory
     */
    public static final String BATIK_CONFIGURATION_SUBDIRECTORY = ".batik";

    /**
     * Name of the Squiggle configuration file
     */
    public static final String SQUIGGLE_CONFIGURATION_FILE = "preferences.xml";

    /**
     * Name of the Squiggle policy file
     */
    public static final String SQUIGGLE_POLICY_FILE = "__svgbrowser.policy";

    /**
     * Entry for granting network access to scripts
     */
    public static final String POLICY_GRANT_SCRIPT_NETWORK_ACCESS
        = "grant {\n  permission java.net.SocketPermission \"*\", \"listen, connect, resolve, accept\";\n};\n\n";

    /**
     * Entry for granting file system access to scripts
     */
    public static final String POLICY_GRANT_SCRIPT_FILE_ACCESS
        = "grant {\n  permission java.io.FilePermission \"<<ALL FILES>>\", \"read\";\n};\n\n";

    /**
     * Entry for the list of recently visited URI
     */
    public static final String PREFERENCE_KEY_VISITED_URI_LIST
        = "preference.key.visited.uri.list";

    /**
     * Entry for the maximum number of last visited URIs
     */
    public static final String PREFERENCE_KEY_VISITED_URI_LIST_LENGTH
        = "preference.key.visited.uri.list.length";

    /**
     * List of separators between URI values in the preference
     * file
     */
    public static final String URI_SEPARATOR = " ";

    /**
     * Default font-family value. 
     */
    public static final String DEFAULT_DEFAULT_FONT_FAMILY 
        = "Arial, Helvetica, sans-serif";

    /**
     * SVG initialization file, used to trigger loading of most of
     * the Batik classes
     */
    public static final String SVG_INITIALIZATION = "resources/init.svg";

    /**
     * Stores the initialization file URI
     */
    protected String svgInitializationURI;

    /**
     * Creates a viewer frame and shows it..
     * @param args The command-line arguments.
     */
    public static void main(String[] args) {
        new Main(args);
    }

    /**
     * The gui resources file name
     */
    public final static String RESOURCES =
        "org.apache.batik.apps.svgbrowser.resources.Main";

    /**
     * URL for Squiggle's security policy file
     */
    public static final String SQUIGGLE_SECURITY_POLICY
        = "org/apache/batik/apps/svgbrowser/resources/svgbrowser.policy"; 

    /**
     * The resource bundle
     */
    protected static ResourceBundle bundle;

    /**
     * The resource manager
     */
    protected static ResourceManager resources;
    static {
        bundle = ResourceBundle.getBundle(RESOURCES, Locale.getDefault());
        resources = new ResourceManager(bundle);
    }

    /**
     * The frame's icon.
     */
    protected static ImageIcon frameIcon = new ImageIcon
        (Main.class.getResource(resources.getString("Frame.icon")));

    /**
     * The preference manager.
     */
    protected XMLPreferenceManager preferenceManager;

    /**
     * Maximum number of recently visited URIs
     */
    public static final int MAX_VISITED_URIS = 10;

    /**
     * The array of last visited URIs
     */
    protected Vector lastVisited = new Vector();

    /**
     * The actual allowed maximum number of last visited URIs
     */
    protected int maxVisitedURIs = MAX_VISITED_URIS;

    /**
     * The arguments.
     */
    protected String[] arguments;

    /**
     * Controls whether the application can override the 
     * system security policy property. This is done when there
     * was no initial security policy specified when the application
     * stated, in which case Batik will use that property.
     */
    protected boolean overrideSecurityPolicy = false;

    /**
     * Script security enforcement is delegated to the 
     * security utility 
     */
    protected ApplicationSecurityEnforcer securityEnforcer;

    /**
     * The option handlers.
     */
    protected Map handlers = new HashMap();
    {
        handlers.put("-font-size", new FontSizeHandler());
    }

    /**
     * The viewer frames.
     */
    protected List viewerFrames = new LinkedList();

    /**
     * The preference dialog.
     */
    protected PreferenceDialog preferenceDialog;

    /**
     * Creates a new application.
     * @param args The command-line arguments.
     */
    public Main(String[] args) {
        arguments = args;

        //
        // Preferences
        //
        Map defaults = new HashMap(11);

        defaults.put(PreferenceDialog.PREFERENCE_KEY_LANGUAGES,
                     Locale.getDefault().getLanguage());
        defaults.put(PreferenceDialog.PREFERENCE_KEY_SHOW_RENDERING,
                     Boolean.FALSE);
        defaults.put(PreferenceDialog.PREFERENCE_KEY_AUTO_ADJUST_WINDOW,
                     Boolean.TRUE);
        defaults.put(PreferenceDialog.PREFERENCE_KEY_SELECTION_XOR_MODE,
                     Boolean.FALSE);
        defaults.put(PreferenceDialog.PREFERENCE_KEY_ENABLE_DOUBLE_BUFFERING,
                     Boolean.TRUE);
        defaults.put(PreferenceDialog.PREFERENCE_KEY_SHOW_DEBUG_TRACE,
                     Boolean.FALSE);
        defaults.put(PreferenceDialog.PREFERENCE_KEY_PROXY_HOST,
                     "");
        defaults.put(PreferenceDialog.PREFERENCE_KEY_PROXY_PORT,
                     "");
        defaults.put(PreferenceDialog.PREFERENCE_KEY_CSS_MEDIA,
                     "screen");
        defaults.put(PreferenceDialog.PREFERENCE_KEY_DEFAULT_FONT_FAMILY,
                     DEFAULT_DEFAULT_FONT_FAMILY);
        defaults.put(PreferenceDialog.PREFERENCE_KEY_IS_XML_PARSER_VALIDATING,
                     Boolean.FALSE);
        defaults.put(PreferenceDialog.PREFERENCE_KEY_ENFORCE_SECURE_SCRIPTING,
                     Boolean.TRUE);
        defaults.put(PreferenceDialog.PREFERENCE_KEY_GRANT_SCRIPT_FILE_ACCESS,
                     Boolean.FALSE);
        defaults.put(PreferenceDialog.PREFERENCE_KEY_GRANT_SCRIPT_NETWORK_ACCESS,
                     Boolean.FALSE);
        defaults.put(PreferenceDialog.PREFERENCE_KEY_LOAD_JAVA,
                     Boolean.TRUE);
        defaults.put(PreferenceDialog.PREFERENCE_KEY_LOAD_ECMASCRIPT,
                     Boolean.TRUE);
        defaults.put(PreferenceDialog.PREFERENCE_KEY_ALLOWED_SCRIPT_ORIGIN,
                     new Integer(ResourceOrigin.DOCUMENT));
        defaults.put(PreferenceDialog.PREFERENCE_KEY_ALLOWED_EXTERNAL_RESOURCE_ORIGIN,
                     new Integer(ResourceOrigin.ANY));
        defaults.put(PREFERENCE_KEY_VISITED_URI_LIST,
                     "");
        defaults.put(PREFERENCE_KEY_VISITED_URI_LIST_LENGTH,
                     new Integer(MAX_VISITED_URIS));
	
        securityEnforcer 
            = new ApplicationSecurityEnforcer(this.getClass(),
                                              SQUIGGLE_SECURITY_POLICY);

        try {
            preferenceManager = new XMLPreferenceManager(SQUIGGLE_CONFIGURATION_FILE,
                                                         defaults);
            String dir = System.getProperty(PROPERTY_USER_HOME);
            File f = new File(dir, BATIK_CONFIGURATION_SUBDIRECTORY);
            f.mkdir();
            preferenceManager.setPreferenceDirectory(f.getCanonicalPath());
            preferenceManager.load();
            setPreferences();
            initializeLastVisited();
            Authenticator.setDefault(new JAuthenticator());
        } catch (Exception e) {
            e.printStackTrace();
        }

        //
        // Initialization
        //
        final AboutDialog initDialog = new AboutDialog();
        final JProgressBar pb = new JProgressBar(0, 3);
        initDialog.getContentPane().add("South", pb);

        // Work around pack() bug on some platforms
        Dimension ss = initDialog.getToolkit().getScreenSize();
        Dimension ds = initDialog.getPreferredSize();

        initDialog.setLocation((ss.width  - ds.width) / 2,
                               (ss.height - ds.height) / 2);

        initDialog.setSize(ds);
        initDialog.setVisible(true);

        final JSVGViewerFrame v = new JSVGViewerFrame(this);
        JSVGCanvas c = v.getJSVGCanvas();
        c.addSVGDocumentLoaderListener(new SVGDocumentLoaderAdapter() {
            public void documentLoadingStarted(SVGDocumentLoaderEvent e) {
                pb.setValue(1);
            }
            public void documentLoadingCompleted(SVGDocumentLoaderEvent e) {
                pb.setValue(2);
            }
        });
        c.addGVTTreeBuilderListener(new GVTTreeBuilderAdapter() {
            public void gvtBuildCompleted(GVTTreeBuilderEvent e) {
                pb.setValue(3);
            }
        });
        c.addGVTTreeRendererListener(new GVTTreeRendererAdapter() {
            public void gvtRenderingCompleted(GVTTreeRendererEvent e) {
                initDialog.dispose();
                v.dispose();
                System.gc();
                run();
            }
        });

        c.setSize(100, 100);
        svgInitializationURI = Main.class.getResource(SVG_INITIALIZATION).toString();
        c.loadSVGDocument(svgInitializationURI);
    }

    /**
     * Installs a custom policy file in the '.batik' directory. This is initialized 
     * with the content of the policy file coming with the distribution
     */
    public void installCustomPolicyFile() throws IOException {
        String securityPolicyProperty 
            = System.getProperty(PROPERTY_JAVA_SECURITY_POLICY);

        if (overrideSecurityPolicy
            ||
            securityPolicyProperty == null
            ||
            "".equals(securityPolicyProperty)) {
            // Access default policy file
            ParsedURL policyURL = new ParsedURL(securityEnforcer.getPolicyURL());
            
            // Override the user policy 
            String dir = System.getProperty(PROPERTY_USER_HOME);
            File batikConfigDir = new File(dir, BATIK_CONFIGURATION_SUBDIRECTORY);
            File policyFile = new File(batikConfigDir, SQUIGGLE_POLICY_FILE);
            
            // Copy original policy file into local policy file
            Reader r = new BufferedReader(new InputStreamReader(policyURL.openStream()));
            Writer w = new FileWriter(policyFile);
            
            char[] buf = new char[1024];
            int n = 0;
            while ( (n=r.read(buf, 0, buf.length)) != -1 ) {
                w.write(buf, 0, n);
            }
            
            r.close();
            
            // Now, append additional grants depending on the security
            // settings
            boolean grantScriptNetworkAccess 
                = preferenceManager.getBoolean
                (PreferenceDialog.PREFERENCE_KEY_GRANT_SCRIPT_NETWORK_ACCESS);
            boolean grantScriptFileAccess
                = preferenceManager.getBoolean
                (PreferenceDialog.PREFERENCE_KEY_GRANT_SCRIPT_FILE_ACCESS);
            
            if (grantScriptNetworkAccess) {
                w.write(POLICY_GRANT_SCRIPT_NETWORK_ACCESS);
            }
            
            if (grantScriptFileAccess) {
                w.write(POLICY_GRANT_SCRIPT_FILE_ACCESS);
            }
            
            w.close();
            
            // We now use the JAVA_SECURITY_POLICY property, so 
            // we allow override on subsequent calls.
            overrideSecurityPolicy = true;
            
            System.setProperty(PROPERTY_JAVA_SECURITY_POLICY,
                               policyFile.toURL().toString());
            
        }
    }

    /**
     * Runs the application.
     */
    public void run() {
        try {
            int i = 0;

            for (; i < arguments.length; i++) {
                OptionHandler oh = (OptionHandler)handlers.get(arguments[i]);
                if (oh == null) {
                    break;
                }
                i = oh.handleOption(i);
            }

            JSVGViewerFrame frame = createAndShowJSVGViewerFrame();
            while (i < arguments.length) {
                if (arguments[i].length() == 0) {
                    i++;
                    continue;
                }

                File file = new File(arguments[i]);
                String uri = null;

                try{
                    if (file.canRead()) {
                        uri = file.toURL().toString();
                    }
                }catch(SecurityException se){
                    // Cannot access files. 
                }
                
                if(uri == null){
                    uri = arguments[i];
                    ParsedURL purl = null;
                    purl = new ParsedURL(arguments[i]);

                    if (!purl.complete())
                        // This is not a valid uri
                        uri = null;
                }

                if (uri != null) {
                    if (frame == null)
                        frame = createAndShowJSVGViewerFrame();

                    frame.showSVGDocument(uri);
                    frame = null;
                } else {
                    // Let the user know that we are
                    // skipping this file...

                    // Note that frame may be null, which is
                    // a valid argument for showMessageDialog

                    // NOTE: Need to revisit Resources/Messages usage to
                    //       have a single entry point. Should have a
                    //       formated message here instead of a + ...
                    JOptionPane.showMessageDialog
                        (frame,
                         resources.getString("Error.skipping.file")
                         + arguments[i]);
                }
                i++;
            }
        } catch (Exception e) {
            e.printStackTrace();
            printUsage();
        }
    }

    /**
     * Prints the command line usage.
     */
    protected void printUsage() {
        System.out.println();

        System.out.println(resources.getString("Command.header"));
        System.out.println(resources.getString("Command.syntax"));
        System.out.println();
        System.out.println(resources.getString("Command.options"));
        Iterator it = handlers.keySet().iterator();
        while (it.hasNext()) {
            String s = (String)it.next();
            System.out.println(((OptionHandler)handlers.get(s)).getDescription());
        }
    }

    /**
     * This interface represents an option handler.
     */
    protected interface OptionHandler {
        /**
         * Handles the current option.
         * @return the index of argument just before the next one to handle.
         */
        int handleOption(int i);

        /**
         * Returns the option description.
         */
        String getDescription();
    }

    /**
     * To handle the '-font-size' option.
     */
    protected class FontSizeHandler implements OptionHandler {
        public int handleOption(int i) {
            int size = Integer.parseInt(arguments[++i]);

            Font font = new Font("Dialog", Font.PLAIN, size);
            FontUIResource fontRes = new FontUIResource(font);
            UIManager.put("CheckBox.font", fontRes);
            UIManager.put("PopupMenu.font", fontRes);
            UIManager.put("TextPane.font", fontRes);
            UIManager.put("MenuItem.font", fontRes);
            UIManager.put("ComboBox.font", fontRes);
            UIManager.put("Button.font", fontRes);
            UIManager.put("Tree.font", fontRes);
            UIManager.put("ScrollPane.font", fontRes);
            UIManager.put("TabbedPane.font", fontRes);
            UIManager.put("EditorPane.font", fontRes);
            UIManager.put("TitledBorder.font", fontRes);
            UIManager.put("Menu.font", fontRes);
            UIManager.put("TextArea.font", fontRes);
            UIManager.put("OptionPane.font", fontRes);
            UIManager.put("DesktopIcon.font", fontRes);
            UIManager.put("MenuBar.font", fontRes);
            UIManager.put("ToolBar.font", fontRes);
            UIManager.put("RadioButton.font", fontRes);
            UIManager.put("RadioButtonMenuItem.font", fontRes);
            UIManager.put("ToggleButton.font", fontRes);
            UIManager.put("ToolTip.font", fontRes);
            UIManager.put("ProgressBar.font", fontRes);
            UIManager.put("TableHeader.font", fontRes);
            UIManager.put("Panel.font", fontRes);
            UIManager.put("List.font", fontRes);
            UIManager.put("ColorChooser.font", fontRes);
            UIManager.put("PasswordField.font", fontRes);
            UIManager.put("TextField.font", fontRes);
            UIManager.put("Table.font", fontRes);
            UIManager.put("Label.font", fontRes);
            UIManager.put("InternalFrameTitlePane.font", fontRes);
            UIManager.put("CheckBoxMenuItem.font", fontRes);

            return i;
        }
        public String getDescription() {
            return resources.getString("Command.font-size");
        }
    }

    // Application ///////////////////////////////////////////////

    /**
     * Creates and shows a new viewer frame.
     */
    public JSVGViewerFrame createAndShowJSVGViewerFrame() {
        JSVGViewerFrame mainFrame = new JSVGViewerFrame(this);
        mainFrame.setSize(resources.getInteger("Frame.width"),
                          resources.getInteger("Frame.height"));
        mainFrame.setIconImage(frameIcon.getImage());
        mainFrame.setTitle(resources.getString("Frame.title"));
        mainFrame.show();
        viewerFrames.add(mainFrame);
        setPreferences(mainFrame);
        return mainFrame;
    }

    /**
     * Closes the given viewer frame.
     */
    public void closeJSVGViewerFrame(JSVGViewerFrame f) {
        f.getJSVGCanvas().stopProcessing();
        viewerFrames.remove(f);
        if (viewerFrames.size() == 0) {
            System.exit(0);
        }
        f.dispose();
    }

    /**
     * Creates a new application exit action.
     */
    public Action createExitAction(JSVGViewerFrame vf) {
        return new AbstractAction() {
                public void actionPerformed(ActionEvent e) {
                    System.exit(0);
                }
            };
    }

    /**
     * Opens the given link in a new window.
     */
    public void openLink(String url) {
        JSVGViewerFrame f = createAndShowJSVGViewerFrame();
        f.getJSVGCanvas().loadSVGDocument(url);
    }

    /**
     * Returns the XML parser class name.
     */
    public String getXMLParserClassName() {
        return XMLResourceDescriptor.getXMLParserClassName();
    }

    /**
     * Returns true if the XML parser must be in validation mode, false
     * otherwise.
     */
    public boolean isXMLParserValidating() {
        return preferenceManager.getBoolean
            (PreferenceDialog.PREFERENCE_KEY_IS_XML_PARSER_VALIDATING);
    }

    /**
     * Shows the preference dialog.
     */
    public void showPreferenceDialog(JSVGViewerFrame f) {
        if (preferenceDialog == null) {
            preferenceDialog = new PreferenceDialog(preferenceManager);
        }
        if (preferenceDialog.showDialog() == PreferenceDialog.OK_OPTION) {
            try {
                preferenceManager.save();
                setPreferences();
            } catch (Exception e) {
            }
        }
    }

    private void setPreferences() throws IOException {
        Iterator it = viewerFrames.iterator();
        while (it.hasNext()) {
            setPreferences((JSVGViewerFrame)it.next());
        }

        System.setProperty("proxyHost", preferenceManager.getString
                           (PreferenceDialog.PREFERENCE_KEY_PROXY_HOST));
        System.setProperty("proxyPort", preferenceManager.getString
                           (PreferenceDialog.PREFERENCE_KEY_PROXY_PORT));

        installCustomPolicyFile();

        securityEnforcer.enforceSecurity
            (preferenceManager.getBoolean
             (PreferenceDialog.PREFERENCE_KEY_ENFORCE_SECURE_SCRIPTING)
             );

    }

    private void setPreferences(JSVGViewerFrame vf) {
        boolean db = preferenceManager.getBoolean
            (PreferenceDialog.PREFERENCE_KEY_ENABLE_DOUBLE_BUFFERING);
        vf.getJSVGCanvas().setDoubleBufferedRendering(db);
        boolean sr = preferenceManager.getBoolean
            (PreferenceDialog.PREFERENCE_KEY_SHOW_RENDERING);
        vf.getJSVGCanvas().setProgressivePaint(sr);
        boolean d = preferenceManager.getBoolean
            (PreferenceDialog.PREFERENCE_KEY_SHOW_DEBUG_TRACE);
        vf.setDebug(d);
        boolean aa = preferenceManager.getBoolean
            (PreferenceDialog.PREFERENCE_KEY_AUTO_ADJUST_WINDOW);
        vf.setAutoAdjust(aa);
        boolean dd = preferenceManager.getBoolean
            (PreferenceDialog.PREFERENCE_KEY_SELECTION_XOR_MODE);
	vf.getJSVGCanvas().setSelectionOverlayXORMode(dd);
    }

    /**
     * Returns the user languages.
     */
    public String getLanguages() {
        String s = preferenceManager.getString
            (PreferenceDialog.PREFERENCE_KEY_LANGUAGES);
        return (s == null)
            ? Locale.getDefault().getLanguage()
            : s;
    }

    /**
     * Returns the user stylesheet uri.
     * @return null if no user style sheet was specified.
     */
    public String getUserStyleSheetURI() {
        return preferenceManager.getString
            (PreferenceDialog.PREFERENCE_KEY_USER_STYLESHEET);
    }

    /**
     * Returns the default value for the CSS
     * "font-family" property
     */
    public String getDefaultFontFamily() {
        return preferenceManager.getString
            (PreferenceDialog.PREFERENCE_KEY_DEFAULT_FONT_FAMILY);
    }

    /**
     * Returns the CSS media to use.
     * @return empty string if no CSS media was specified.
     */
    public String getMedia() {
        String s = preferenceManager.getString
            (PreferenceDialog.PREFERENCE_KEY_CSS_MEDIA);
        return (s == null) ? "screen" : s;
    }

    /**
     * Returns true if the selection overlay is painted in XOR mode, false
     * otherwise.
     */
    public boolean isSelectionOverlayXORMode() {
        return preferenceManager.getBoolean
            (PreferenceDialog.PREFERENCE_KEY_SELECTION_XOR_MODE);
    }

    /**
     * Returns true if the input scriptType can be loaded in
     * this application.
     */
    public boolean canLoadScriptType(String scriptType){
        if (SVGConstants.SVG_SCRIPT_TYPE_ECMASCRIPT.equals(scriptType)){
            return preferenceManager.getBoolean
                (PreferenceDialog.PREFERENCE_KEY_LOAD_ECMASCRIPT);
        } else if (SVGConstants.SVG_SCRIPT_TYPE_JAVA.equals(scriptType)){
            return preferenceManager.getBoolean
                (PreferenceDialog.PREFERENCE_KEY_LOAD_JAVA);
        } else {
            return preferenceManager.getBoolean
                (scriptType + UNKNOWN_SCRIPT_TYPE_LOAD_KEY_EXTENSION);
        }
    }

    /**
     * Returns the allowed origins for scripts.
     * @see ResourceOrigin
     */
    public int getAllowedScriptOrigin() {
        int ret = preferenceManager.getInteger
            (PreferenceDialog.PREFERENCE_KEY_ALLOWED_SCRIPT_ORIGIN);

        return ret;
    }

    /**
     * Returns the allowed origins for external
     * resources. 
     * @see ResourceOrigin
     */
    public int getAllowedExternalResourceOrigin() {
        int ret = preferenceManager.getInteger
            (PreferenceDialog.PREFERENCE_KEY_ALLOWED_EXTERNAL_RESOURCE_ORIGIN);

        return ret;
    }

    /**
     * Notifies Application of recently visited URI
     */ 
    public void addVisitedURI(String uri) {
        if(svgInitializationURI.equals(uri)) {
            return;
        }
        
        int maxVisitedURIs = 
            preferenceManager.getInteger
            (PREFERENCE_KEY_VISITED_URI_LIST_LENGTH);
        
        if (maxVisitedURIs < 0) {
            maxVisitedURIs = 0;
        }

        if (lastVisited.contains(uri)) {
            lastVisited.removeElement(uri);
        }

        while (lastVisited.size() > 0 && lastVisited.size() > (maxVisitedURIs-1)) {
            lastVisited.removeElementAt(0);
        } 

        if (maxVisitedURIs > 0) {
            lastVisited.addElement(uri);
        }

        // Now, save the list of visited URL into the preferences
        StringBuffer lastVisitedBuffer = new StringBuffer();

        for (int i=0; i<lastVisited.size(); i++) {
            lastVisitedBuffer.append(URLEncoder.encode(lastVisited.elementAt(i).toString()));
            lastVisitedBuffer.append(URI_SEPARATOR);
        }
        
        preferenceManager.setString
            (PREFERENCE_KEY_VISITED_URI_LIST,
             lastVisitedBuffer.toString());

        try {
            preferenceManager.save();
        } catch (Exception e) {
            // As in other places. But this is ugly...
        }
    }

    /**
     * Asks Application for a list of recently visited URI.
     */
    public String[] getVisitedURIs() {
        String[] visitedURIs = new String[lastVisited.size()];
        lastVisited.copyInto(visitedURIs);
        return visitedURIs;
    }

    /**
     * Initializes the lastVisited array
     */
    protected void initializeLastVisited(){
        String lastVisitedStr 
            = preferenceManager.getString(PREFERENCE_KEY_VISITED_URI_LIST);

        StringTokenizer st 
            = new StringTokenizer(lastVisitedStr,
                                  URI_SEPARATOR);

        int n = st.countTokens();

        int maxVisitedURIs 
            = preferenceManager.getInteger
            (PREFERENCE_KEY_VISITED_URI_LIST_LENGTH);

        if (n > maxVisitedURIs) {
            n = maxVisitedURIs;
        }

        for (int i=0; i<n; i++) {
            lastVisited.addElement(URLDecoder.decode(st.nextToken()));
        }
    }
}
