现在的位置: 首页 > 综合 > 正文

worldwind学习笔记-1-Configuration

2013年10月12日 ⁄ 综合 ⁄ 共 23367字 ⁄ 字号 评论关闭

做的一个小的cs程序中一直嵌套worldwind使用,由于使用的是worldwind的java版,结果cs程序也一直是用java的swing开发的,swing在几年前确实很强大,但是现在给人感觉蛮没落的,几乎很少见到swing开发的软件了,不过还好,我们的用户不考虑我们使用什么系统,也可以吹吹牛逼说,我们的程序可以在mac下,linux下,windows下运行,当然,事实也确实如此。

不过我们使用worldwind一直局限在使用它的功能上,一直没能深入进去研究其架构等,我觉得,ww这款sdk功能真的很强大啊,但是我们只用到了collada下的东西做模型,surfaceImage做贴图,animation做3D云,surface下还用了写东西,用来简单的画画图形,画画path等,当然,我们也使用了OpelGL中的一些功能,画了爆炸,降雨等效果,但是我感觉都不是很理想,显然,OpenGL是基础,这个也得会,才能看懂worldwind中的一些代码。

好了,一些基础信息介绍完毕,我想开始去深入挖掘下ww内的东西,尝试学习他,而不仅仅是使用它。

我选择使用HelloWorldWind.java这个类作为入口,因为它只有一个最基本的球,通过阅读代码去学习,我觉得还是可以有较快的进步的。

先来看下这个类的代码。

package gov.nasa.worldwindx;

import gov.nasa.worldwind.BasicModel;
import gov.nasa.worldwind.Configuration;
import gov.nasa.worldwind.awt.WorldWindowGLCanvas;

public class HelloWorldWind
{
    // An inner class is used rather than directly subclassing JFrame in the main class so
    // that the main can configure system properties prior to invoking Swing. This is
    // necessary for instance on OS X (Macs) so that the application name can be specified.

    private static class AppFrame extends javax.swing.JFrame
    {
        public AppFrame()
        {
        	 
            this.setDefaultCloseOperation(EXIT_ON_CLOSE);
            WorldWindowGLCanvas wwd = new WorldWindowGLCanvas();
            wwd.setPreferredSize(new java.awt.Dimension(1000, 800));
            this.getContentPane().add(wwd, java.awt.BorderLayout.CENTER);
             
            wwd.setModel(new BasicModel());
            this.pack();
        }
    }

    public static void main(String[] args)
    {
        if (Configuration.isMacOS())
        {
            System.setProperty("com.apple.mrj.application.apple.menu.about.name", "Hello World Wind");
        }
        final AppFrame app=new AppFrame();
        java.awt.EventQueue.invokeLater(new Runnable()
        {
            public void run()
            {
                // Create an AppFrame and immediately make it visible. As per Swing convention, this
                // is done within an invokeLater call so that it executes on an AWT thread.
                
                app.setVisible(true);
            }
        });
    }
}

代码非常的简单,就是构建了一个JFrame,然后调用awt包中的EventQueue来执行,之所以setvisible是单独执行的,是因为worldwind底层要调用jogl,从而用opengl画图。这个代码我曾尝试将app.setVisible拿出来执行,而不是放在EventQueue里面。这种情况在win32下可以正常,在win64下就不正常,按照EventQueue的解释(awt是单线程的所有awt的组件只能在事件处理线程中访问,从而保证组件状态的可确定性)。。。。。这一块我只想说四个字:不觉明历。。。。貌似是和Java内存模型中的,所谓的无关对象的执行顺序会重排也有一定的关系吧。谁能给点这方面的建议就好了。。

再贴一段别人的解释:使用eventqueue.invokelater()好处是显而易见的,这个方法调用完毕后,它会被销毁,因为匿名内部类是作为临时变量存在的,给它分配的内存在此时会被释放。这个对于只需要在一个地方使用时可以节省内存,而且这个类是不可以被其它的方法或类使用的,只能被EventQueue.invokeLater()来使用。但如果你需要一个在很多地方都能用到的类,而不是只在某一个类里面或者方法里用的话,定义成匿名内部类显然是不可取的。 是,runnable是跟线程相关的类。
swingutilities.invokelater()和eventqueue.invokelater(),后者可以不干扰到事件分发线程.SwingUtilities版只是一个薄薄的封装方法,它直接转而调用 EventQueue.invokeLater。因为Swing框架本身经常调用SwingUtilities,使用SwingUtilities可以减少程序引入的类。

对于如此多的概念,我感觉好无力,还是看Main里面干嘛了吧。

if (Configuration.isMacOS()) 第一句,判断是不是苹果的操作系统,我不是苹果机,爱我吊事。。。不过,Configuration却是一个非常非常重要的东西,它是不可忽略的。

Configuration的代码如下。

/*
 * Copyright (C) 2012 United States Government as represented by the Administrator of the
 * National Aeronautics and Space Administration.
 * All Rights Reserved.
 */

package gov.nasa.worldwind;

import gov.nasa.worldwind.avlist.AVKey;
import gov.nasa.worldwind.geom.Angle;
import gov.nasa.worldwind.util.*;
import org.w3c.dom.*;

import javax.media.opengl.GLProfile;
import javax.xml.xpath.*;
import java.io.*;
import java.util.*;
import java.util.logging.Level;

/**
 * This class manages the initial World Wind configuration. It reads World Wind configuration files and registers their
 * contents. Configurations files contain the names of classes to create at run-time, the initial model definition,
 * including the globe, elevation model and layers, and various control quantities such as cache sizes and data
 * retrieval timeouts.
 * <p/>
 * The Configuration class is a singleton, but its instance is not exposed publicly. It is addressed only via static
 * methods of the class. It is constructed upon first use of any of its static methods.
 * <p/>
 * When the Configuration class is first instantiated it reads the XML document <code>config/worldwind.xml</code> and
 * registers all the information there. The information can subsequently be retrieved via the class' various
 * <code>getValue</code> methods. Many World Wind start-up objects query this information to determine the classes to
 * create. For example, the first World Wind object created by an application is typically a {@link
 * gov.nasa.worldwind.awt.WorldWindowGLCanvas}. During construction that class causes World Wind's internal classes to
 * be constructed, using the names of those classes drawn from the Configuration singleton, this class.
 * <p/>
 * The default World Wind configuration document is <code>config/worldwind.xml</code>. This can be changed by setting
 * the Java property <code>gov.nasa.worldwind.config.file</code> to a different file name or a valid URL prior to
 * creating any World Wind object or invoking any static methods of World Wind classes, including the Configuration
 * class. When an application specifies a different configuration location it typically does so in its main method prior
 * to using World Wind. If a file is specified its location must be on the classpath. (The contents of application and
 * World Wind jar files are typically on the classpath, in which case the configuration file may be in the jar file.)
 * <p/>
 * Additionally, an application may set another Java property, <code>gov.nasa.worldwind.app.config.document</code>, to a
 * file name or URL whose contents contain configuration values to override those of the primary configuration document.
 * World Wind overrides only those values in this application document, it leaves all others to the value specified in
 * the primary document. Applications usually specify an override document in order to specify the initial layers in the
 * model.
 * <p/>
 * See <code>config/worldwind.xml</code> for documentation on setting configuration values.
 * <p/>
 * Configuration values can also be set programatically via {@link Configuration#setValue(String, Object)}, but they are
 * not retroactive so affect only Configuration queries made subsequent to setting the value.
 * <p/>
 * <em>Note:</em> Prior to September of 2009, configuration properties were read from the file
 * <code>config/worldwind.properties</code>. An alternate file could be specified via the
 * <code>gov.nasa.worldwind.config.file</code> Java property. These mechanisms remain available but are deprecated.
 * World Wind no longer contains a <code>worldwind.properties</code> file. If <code>worldwind.properties</code> or its
 * replacement as specified through the Java property exists at run-time and can be found via the classpath,
 * configuration values specified by that mechanism are given precedence over values specified by the new mechanism.
 *
 * @version $Id: Configuration.java 1171 2013-02-11 21:45:02Z dcollins $
 * @author Tom Gaskins
 */
public class Configuration // Singleton
{
    public static final String DEFAULT_LOGGER_NAME = "gov.nasa.worldwind";

    private static final String CONFIG_PROPERTIES_FILE_NAME = "config/worldwind.properties";
    private static final String CONFIG_FILE_PROPERTY_KEY = "gov.nasa.worldwind.config.file";

    private static final String CONFIG_WW_DOCUMENT_KEY = "gov.nasa.worldwind.config.document";
    private static final String CONFIG_WW_DOCUMENT_NAME = "config/worldwind.xml";

    private static final String CONFIG_APP_DOCUMENT_KEY = "gov.nasa.worldwind.app.config.document";

    private static Configuration ourInstance = new Configuration();

    private static Configuration getInstance()
    {
        return ourInstance;
    }

    private final Properties properties;
    private final ArrayList<Document> configDocs = new ArrayList<Document>();

    /** Private constructor invoked only internally. */
    private Configuration()
    {
        this.properties = initializeDefaults();

        // Load the app's configuration if there is one
        try
        {
            String appConfigLocation = System.getProperty(CONFIG_APP_DOCUMENT_KEY);
            if (appConfigLocation != null)
                this.loadConfigDoc(System.getProperty(CONFIG_APP_DOCUMENT_KEY)); // Load app's config first
        }
        catch (Exception e)
        {
            Logging.logger(DEFAULT_LOGGER_NAME).log(Level.WARNING, "Configuration.ConfigNotFound",
                System.getProperty(CONFIG_APP_DOCUMENT_KEY));
            // Don't stop if the app config file can't be found or parsed
        }

        try
        {
            // Load the default configuration
            this.loadConfigDoc(System.getProperty(CONFIG_WW_DOCUMENT_KEY, CONFIG_WW_DOCUMENT_NAME));

            // Load config properties, ensuring that the app's config takes precedence over wwj's
            for (int i = this.configDocs.size() - 1; i >= 0; i--)
            {
                this.loadConfigProperties(this.configDocs.get(i));
            }
        }
        catch (Exception e)
        {
            Logging.logger(DEFAULT_LOGGER_NAME).log(Level.WARNING, "Configuration.ConfigNotFound",
                System.getProperty(CONFIG_WW_DOCUMENT_KEY));
        }

        // To support old-style configuration, read an existing config properties file and give the properties
        // specified there precedence.
        this.initializeCustom();
    }

    private void loadConfigDoc(String configLocation)
    {
        if (!WWUtil.isEmpty(configLocation))
        {
            Document doc = WWXML.openDocument(configLocation);
            if (doc != null)
            {
                this.configDocs.add(doc);
//                this.loadConfigProperties(doc);
            }
        }
    }

    private void insertConfigDoc(String configLocation)
    {
        if (!WWUtil.isEmpty(configLocation))
        {
            Document doc = WWXML.openDocument(configLocation);
            if (doc != null)
            {
                this.configDocs.add(0, doc);
                this.loadConfigProperties(doc);
            }
        }
    }

    private void loadConfigProperties(Document doc)
    {
        try
        {
            XPath xpath = WWXML.makeXPath();

            NodeList nodes = (NodeList) xpath.evaluate("/WorldWindConfiguration/Property", doc, XPathConstants.NODESET);
            if (nodes == null || nodes.getLength() == 0)
                return;

            for (int i = 0; i < nodes.getLength(); i++)
            {
                Node node = nodes.item(i);
                String prop = xpath.evaluate("@name", node);
                String value = xpath.evaluate("@value", node);
                if (WWUtil.isEmpty(prop))// || WWUtil.isEmpty(value))
                    continue;

                this.properties.setProperty(prop, value);
            }
        }
        catch (XPathExpressionException e)
        {
            Logging.logger(DEFAULT_LOGGER_NAME).log(Level.WARNING, "XML.ParserConfigurationException");
        }
    }

    private Properties initializeDefaults()
    {
        Properties defaults = new Properties();
        java.util.TimeZone tz = java.util.Calendar.getInstance().getTimeZone();
        if (tz != null)
            defaults.setProperty(AVKey.INITIAL_LONGITUDE,
                Double.toString(
                    Angle.fromDegrees(180.0 * tz.getOffset(System.currentTimeMillis()) / (12.0 * 3.6e6)).degrees));
        return defaults;
    }

    private void initializeCustom()
    {
        // IMPORTANT NOTE: Always use the single argument version of Logging.logger in this method because the non-arg
        // method assumes an instance of Configuration already exists.

        String configFileName = System.getProperty(CONFIG_FILE_PROPERTY_KEY, CONFIG_PROPERTIES_FILE_NAME);
        try
        {
            java.io.InputStream propsStream = null;
            File file = new File(configFileName);
            if (file.exists())
            {
                try
                {
                    propsStream = new FileInputStream(file);
                }
                catch (FileNotFoundException e)
                {
                    Logging.logger(DEFAULT_LOGGER_NAME).log(Level.FINEST, "Configuration.LocalConfigFileNotFound",
                        configFileName);
                }
            }

            if (propsStream == null)
            {
                propsStream = this.getClass().getResourceAsStream("/" + configFileName);
            }

            if (propsStream != null)
                this.properties.load(propsStream);
        }
        // Use a named logger in all the catch statements below to prevent Logger from calling back into
        // Configuration when this Configuration instance is not yet fully instantiated.
        catch (IOException e)
        {
            Logging.logger(DEFAULT_LOGGER_NAME).log(Level.SEVERE, "Configuration.ExceptionReadingPropsFile", e);
        }
    }

    public static void insertConfigurationDocument(String fileName)
    {
        getInstance().insertConfigDoc(fileName);
    }

    /**
     * Return as a string the value associated with a specified key.
     *
     * @param key          the key for the desired value.
     * @param defaultValue the value to return if the key does not exist.
     *
     * @return the value associated with the key, or the specified default value if the key does not exist.
     */
    public static synchronized String getStringValue(String key, String defaultValue)
    {
        String v = getStringValue(key);
        return v != null ? v : defaultValue;
    }

    /**
     * Return as a string the value associated with a specified key.
     *
     * @param key the key for the desired value.
     *
     * @return the value associated with the key, or null if the key does not exist.
     */
    public static synchronized String getStringValue(String key)
    {
        Object o = getInstance().properties.getProperty(key);
        return o != null ? o.toString() : null;
    }

    /**
     * Return as an Integer the value associated with a specified key.
     *
     * @param key          the key for the desired value.
     * @param defaultValue the value to return if the key does not exist.
     *
     * @return the value associated with the key, or the specified default value if the key does not exist or is not an
     *         Integer or string representation of an Integer.
     */
    public static synchronized Integer getIntegerValue(String key, Integer defaultValue)
    {
        Integer v = getIntegerValue(key);
        return v != null ? v : defaultValue;
    }

    /**
     * Return as an Integer the value associated with a specified key.
     *
     * @param key the key for the desired value.
     *
     * @return the value associated with the key, or null if the key does not exist or is not an Integer or string
     *         representation of an Integer.
     */
    public static synchronized Integer getIntegerValue(String key)
    {
        String v = getStringValue(key);
        if (v == null)
            return null;

        try
        {
            return Integer.parseInt(v);
        }
        catch (NumberFormatException e)
        {
            Logging.logger().log(Level.SEVERE, "Configuration.ConversionError", v);
            return null;
        }
    }

    /**
     * Return as an Long the value associated with a specified key.
     *
     * @param key          the key for the desired value.
     * @param defaultValue the value to return if the key does not exist.
     *
     * @return the value associated with the key, or the specified default value if the key does not exist or is not a
     *         Long or string representation of a Long.
     */
    public static synchronized Long getLongValue(String key, Long defaultValue)
    {
        Long v = getLongValue(key);
        return v != null ? v : defaultValue;
    }

    /**
     * Return as an Long the value associated with a specified key.
     *
     * @param key the key for the desired value.
     *
     * @return the value associated with the key, or null if the key does not exist or is not a Long or string
     *         representation of a Long.
     */
    public static synchronized Long getLongValue(String key)
    {
        String v = getStringValue(key);
        if (v == null)
            return null;

        try
        {
            return Long.parseLong(v);
        }
        catch (NumberFormatException e)
        {
            Logging.logger().log(Level.SEVERE, "Configuration.ConversionError", v);
            return null;
        }
    }

    /**
     * Return as an Double the value associated with a specified key.
     *
     * @param key          the key for the desired value.
     * @param defaultValue the value to return if the key does not exist.
     *
     * @return the value associated with the key, or the specified default value if the key does not exist or is not an
     *         Double or string representation of an Double.
     */
    public static synchronized Double getDoubleValue(String key, Double defaultValue)
    {
        Double v = getDoubleValue(key);
        return v != null ? v : defaultValue;
    }

    /**
     * Return as an Double the value associated with a specified key.
     *
     * @param key the key for the desired value.
     *
     * @return the value associated with the key, or null if the key does not exist or is not an Double or string
     *         representation of an Double.
     */
    public static synchronized Double getDoubleValue(String key)
    {
        String v = getStringValue(key);
        if (v == null)
            return null;

        try
        {
            return Double.parseDouble(v);
        }
        catch (NumberFormatException e)
        {
            Logging.logger().log(Level.SEVERE, "Configuration.ConversionError", v);
            return null;
        }
    }

    /**
     * Return as a Boolean the value associated with a specified key.
     * <p/>
     * Valid values for true are '1' or anything that starts with 't' or 'T'. ie. 'true', 'True', 't' Valid values for
     * false are '0' or anything that starts with 'f' or 'F'. ie. 'false', 'False', 'f'
     *
     * @param key          the key for the desired value.
     * @param defaultValue the value to return if the key does not exist.
     *
     * @return the value associated with the key, or the specified default value if the key does not exist or is not a
     *         Boolean or string representation of an Boolean.
     */
    public static synchronized Boolean getBooleanValue(String key, Boolean defaultValue)
    {
        Boolean v = getBooleanValue(key);
        return v != null ? v : defaultValue;
    }

    /**
     * Return as a Boolean the value associated with a specified key.
     * <p/>
     * Valid values for true are '1' or anything that starts with 't' or 'T'. ie. 'true', 'True', 't' Valid values for
     * false are '0' or anything that starts with 'f' or 'F'. ie. 'false', 'False', 'f'
     *
     * @param key the key for the desired value.
     *
     * @return the value associated with the key, or null if the key does not exist or is not a Boolean or string
     *         representation of an Boolean.
     */
    public static synchronized Boolean getBooleanValue(String key)
    {
        String v = getStringValue(key);
        if (v == null)
            return null;

        if (v.trim().toUpperCase().startsWith("T") || v.trim().equals("1"))
        {
            return true;
        }
        else if (v.trim().toUpperCase().startsWith("F") || v.trim().equals("0"))
        {
            return false;
        }
        else
        {
            Logging.logger().log(Level.SEVERE, "Configuration.ConversionError", v);
            return null;
        }
    }

    /**
     * Determines whether a key exists in the configuration.
     *
     * @param key the key of interest.
     *
     * @return true if the key exists, otherwise false.
     */
    public static synchronized boolean hasKey(String key)
    {
        return getInstance().properties.contains(key);
    }

    /**
     * Removes a key and its value from the configuration if the configuration contains the key.
     *
     * @param key the key of interest.
     */
    public static synchronized void removeKey(String key)
    {
        getInstance().properties.remove(key);
    }

    /**
     * Adds a key and value to the configuration, or changes the value associated with the key if the key is already in
     * the configuration.
     *
     * @param key   the key to set.
     * @param value the value to associate with the key.
     */
    public static synchronized void setValue(String key, Object value)
    {
        getInstance().properties.put(key, value.toString());
    }

    // OS, user, and run-time specific system properties. //

    /**
     * Returns the path to the application's current working directory.
     *
     * @return the absolute path to the application's current working directory.
     */
    public static String getCurrentWorkingDirectory()
    {
        String dir = System.getProperty("user.dir");
        return (dir != null) ? dir : ".";
    }

    /**
     * Returns the path to the application user's home directory.
     *
     * @return the absolute path to the application user's home directory.
     */
    public static String getUserHomeDirectory()
    {
        String dir = System.getProperty("user.home");
        return (dir != null) ? dir : ".";
    }

    /**
     * Returns the path to the operating system's temp directory.
     *
     * @return the absolute path to the operating system's temporary directory.
     */
    public static String getSystemTempDirectory()
    {
        String dir = System.getProperty("java.io.tmpdir");
        return (dir != null) ? dir : ".";
    }

    /**
     * Returns the path to the current user's application data directory. The path returned depends on the operating
     * system on which the Java Virtual Machine is running. The following table provides the path for all supported
     * operating systems:
     * <p/>
     * <table> <tr><th>Operating System</th><th>Path</th></tr> <tr><td>Mac OS X</td><td>~/Library/Application
     * Support</td></tr> <tr><td>Windows</td><td>~\\Application Data</td></tr> <tr><td>Linux, Unix,
     * Solaris</td><td>~/</td></tr> </table>
     *
     * @return the absolute path to the current user's application data directory.
     */
    public static String getCurrentUserAppDataDirectory()
    {
        if (isMacOS())
        {
            // Return a path that Mac OS X has designated for app-specific data and support files. See the following URL
            // for details:
            // http://developer.apple.com/library/mac/#documentation/FileManagement/Conceptual/FileSystemProgrammingGUide/MacOSXDirectories/MacOSXDirectories.html#//apple_ref/doc/uid/TP40010672-CH10-SW1
            return getUserHomeDirectory() + "/Library/Application Support";
        }
        else if (isWindowsOS())
        {
            return getUserHomeDirectory() + "\\Application Data";
        }
        else if (isLinuxOS() || isUnixOS() || isSolarisOS())
        {
            return getUserHomeDirectory();
        }
        else
        {
            String msg = Logging.getMessage("generic.UnknownOperatingSystem");
            Logging.logger().fine(msg);
            return null;
        }
    }

    /**
     * Determines whether the operating system is a Mac operating system.
     *
     * @return true if the operating system is a Mac operating system, otherwise false.
     */
    public static boolean isMacOS()
    {
        String osName = System.getProperty("os.name");
        return osName != null && osName.toLowerCase().contains("mac");
    }

    /**
     * Determines whether the operating system is Windows operating system.
     *
     * @return true if the operating system is a Windows operating system, otherwise false.
     */
    public static boolean isWindowsOS()
    {
        String osName = System.getProperty("os.name");
        return osName != null && osName.toLowerCase().contains("windows");
    }

    /**
     * Determines whether the operating system is Windows XP operating system.
     *
     * @return true if the operating system is a Windows XP operating system, otherwise false.
     */
    public static boolean isWindowsXPOS()
    {
        String osName = System.getProperty("os.name");
        return osName != null && osName.toLowerCase().contains("windows") && osName.contains("xp");
    }

    /**
     * Determines whether the operating system is Windows Vista operating system.
     *
     * @return true if the operating system is a Windows Vista operating system, otherwise false.
     */
    public static boolean isWindowsVistaOS()
    {
        String osName = System.getProperty("os.name");
        return osName != null && osName.toLowerCase().contains("windows") && osName.contains("vista");
    }

    /**
     * Determines whether the operating system is Windows 7 operating system.
     *
     * @return true if the operating system is a Windows Vista operating system, otherwise false.
     */
    public static boolean isWindows7OS()
    {
        String osName = System.getProperty("os.name");
        return osName != null && osName.toLowerCase().contains("windows") && osName.contains("7");
    }

    /**
     * Determines whether the operating system is Linux operating system.
     *
     * @return true if the operating system is a Linux operating system, otherwise false.
     */
    public static boolean isLinuxOS()
    {
        String osName = System.getProperty("os.name");
        return osName != null && osName.toLowerCase().contains("linux");
    }

    /**
     * Determines whether the operating system is Unix operating system.
     *
     * @return true if the operating system is a Unix operating system, otherwise false.
     */
    public static boolean isUnixOS()
    {
        String osName = System.getProperty("os.name");
        return osName != null && osName.toLowerCase().contains("unix");
    }

    /**
     * Determines whether the operating system is Solaris operating system.
     *
     * @return true if the operating system is a Solaris operating system, otherwise false.
     */
    public static boolean isSolarisOS()
    {
        String osName = System.getProperty("os.name");
        return osName != null && osName.toLowerCase().contains("solaris");
    }

    /**
     * Returns the version of the Java virtual machine.
     *
     * @return the Java virtual machine version.
     */
    public static float getJavaVersion()
    {
        float ver = 0f;
        String s = System.getProperty("java.specification.version");
        if (null == s || s.length() == 0)
            s = System.getProperty("java.version");
        try
        {
            ver = Float.parseFloat(s.trim());
        }
        catch (NumberFormatException ignore)
        {
        }
        return ver;
    }

    /**
     * Returns the highest OpenGL profile available on the current graphics device that is compatible with World Wind.
     * The returned profile favors hardware acceleration over software acceleration. With JOGL version 2.0-rc11, this
     * returns the highest available profile from the following list:
     * <p/>
     * <ul> <li>OpenGL compatibility profile 4.x</li> <li>OpenGL compatibility profile 3.x</li> <li>OpenGL profile 1.x
     * up to 3.0</li> </ul>
     *
     * @return the highest compatible OpenGL profile.
     */
    public static GLProfile getMaxCompatibleGLProfile()
    {
        return GLProfile.getMaxFixedFunc(true); // Favor a hardware rasterizer.
    }

    /**
     * Returns a specified element of an XML configuration document.
     *
     * @param xpathExpression an XPath expression identifying the element of interest.
     *
     * @return the element of interest if the XPath expression is valid and the element exists, otherwise null.
     *
     * @throws NullPointerException if the XPath expression is null.
     */
    public static Element getElement(String xpathExpression)
    {
        XPath xpath = WWXML.makeXPath();

        for (Document doc : getInstance().configDocs)
        {
            try
            {
                Node node = (Node) xpath.evaluate(xpathExpression, doc.getDocumentElement(), XPathConstants.NODE);
                if (node != null)
                    return (Element) node;
            }
            catch (XPathExpressionException e)
            {
                return null;
            }
        }

        return null;
    }
}

很轻松的在前几行就可以看到,这是单列的,整个程序就此一家,别无他处,所有的方法几乎都是static声明的,外部可以直接调用。

    private static Configuration getInstance()
    {
        return ourInstance;
    }

其所返回的ourInstance在这个方法的上面声明的,也是static的,并且调用了私有的构造参数初始化。

构造函数中首先就是先给Properties初始化,这个对象是util包中的,用于存储一些属性变量。

private Properties initializeDefaults()
    {
        Properties defaults = new Properties();
        java.util.TimeZone tz = java.util.Calendar.getInstance().getTimeZone();
        if (tz != null)
            defaults.setProperty(AVKey.INITIAL_LONGITUDE,
                Double.toString(
                    Angle.fromDegrees(180.0 * tz.getOffset(System.currentTimeMillis()) / (12.0 * 3.6e6)).degrees));
        return defaults;
    }

这段代码,给defaults增加了一个开始的经纬度,这个经纬度,是根据当前程序所运行的国家的时区以及当前时间,以及180分成12个时区,每个时区所分的时间等信息计算得到的,我的结果是120°。

之后它在try中开始载入配置文件:

String appConfigLocation = System.getProperty(CONFIG_APP_DOCUMENT_KEY);
            if (appConfigLocation != null)
                this.loadConfigDoc(System.getProperty(CONFIG_APP_DOCUMENT_KEY));

appConfiguration的结果是null,所以不执行哈。

再然后有一个 this.loadConfigDoc(System.getProperty(CONFIG_WW_DOCUMENT_KEY, CONFIG_WW_DOCUMENT_NAME));

追一下System的源代码,里面还有一层,最终找到这样的代码

public String getProperty(String key, String defaultValue) {
        String val = getProperty(key);
        return (val == null) ? defaultValue : val;
    }

loadConfigDoc这个方法,是读取xml文件的,这个里面深入下,可以看到有趣的东西,就是

public static Document openDocument(Object docSource)
    {
        if (docSource == null || WWUtil.isEmpty(docSource))
        {
            String message = Logging.getMessage("nullValue.DocumentSourceIsNull");
            throw new IllegalArgumentException(message);
        }

        if (docSource instanceof URL)
        {
            return openDocumentURL((URL) docSource);
        }
        else if (docSource instanceof InputStream)
        {
            return openDocumentStream((InputStream) docSource);
        }
        else if (docSource instanceof File)
        {
            return openDocumentFile(((File) docSource).getPath(), null);
        }
        else if (!(docSource instanceof String))
        {
            String message = Logging.getMessage("generic.UnrecognizedSourceType", docSource.toString());
            throw new IllegalArgumentException(message);
        }

        String sourceName = (String) docSource;

        URL url = WWIO.makeURL(sourceName);
        if (url != null)
            return openDocumentURL(url);

        return openDocumentFile(sourceName, null);
    }

比较牛的代码啊,是吧?!类似于工厂模式,按照传过来的不同的对象,用不同的方式去打开,都返回Document的实例,而且通过最后一句if判断是不是String,如果是String,就用URL的方式打开。读取XML文件是个老生长谈的问题,这里他用的JAXP读取的,不过百度说:由于问题太多,从JDK1.7开始删除该功能。

之后呢,要把刚读到的Document对象用loadConfigProperties方法,把里面的内容放入property对象中,就实现了系统的初始化。

注意,构造函数中最后一句的this.initializeCustom();没有啥意思了,是老版本中读取配置文件的,也可以用来自定义一些内容。

抱歉!评论已关闭.