Arch. Discuss of Plugin System

From PCGen Wiki
Revision as of 14:56, 2 September 2008 by Karianna (talk | contribs) (New page: {| align="right" | __TOC__ |} =Introduction= This document is current as of build 1501 (October 20, 2006), an unreleased version prior to PCGen 5.11.1 (Alpha) *NOTE* Unless they are...)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

Introduction

This document is current as of build 1501 (October 20, 2006), an unreleased version prior to PCGen 5.11.1 (Alpha)

  • NOTE* Unless they are ambiguous, method references are always followed by a null parameter list () even if there actually parameters in the Java method call. This is for reading ease of this document and should not be taken as a literal interpretation of the code.

Data that defines a Class, Race, Language, and other characteristics and attributes of a PlayerCharacter are stored in LST files. These LST files are parsed to import the information from those LST files into objects within the PC-Gen program. This is an attempt to provide some context on how the import process works (and some basis for the existing architecture) in order to help interpret code contained within the plugin.* Classes.

Overview of the Plugin System

JARs that are within the Plugin directory are parsed for their contents. This actually happens in the gmgen.pluginmgr.JARClassLoader Class. As one of many operations that takes place during the import, each Class is analyzed to determine if it is a persistence Token (a persistence Token is defined as a non-abstract Class that implements the LstToken interface). When a persistence Token is found, it is imported into a special map of persistence Tokens.

This 'special map' is effectively a Map with two keys (or a Map to a Map for those who want to know the technical structure in the code). The first key is the Interface implemented by the Token. This identifies which file types the Token is capable of processing. The second key is the String version of the Token (as it will be found in an LST file). With those two keys, the actual object which can parse the String from the LST file into the internal representation in PCGen can be resolved.

The storage location for this 'special map' is the pcgen.persistence.lst.TokenStore Class. TokenStore stores the available Tokens, based on their Interface. TokenStore can then be queried by individual *Loader classes to determine what Token Class to use to import a specific token string in an LST file.

LST import

Each LST file type has an associated *Loader class within the pcgen.persistence.lst package. Spells, for example, are loaded using the SpellLoader class.

Token conflicts

How are token conflicts resolved? If two tokens have the same key (String before the : in the LST file), AND implement the same persistence Token Interface (e.g. PCClassLSTToken), then an error will be reported by the TokenStore class when the plugin JAR files are loaded.

In some cases there are both Global tags and "local" tags that have the same key (e.g. "TEMPLATE"). In this case, the *Loader class will determine the priority. Generally it makes sense that the "local" key (one that is specific to a certain type of LST file) would take priority over the Global Token. This is the case with TEMPLATE, as the Global tag processing takes place in a call to PObjectLoader.parseTagLevel(), far below the PCClass-specific processing that takes place early in PCClassLoader.parseClassLine()

Export Tokens

How are tokens exported? This varies by what the Token is. Some Tokens have an export token in plugin.exporttokens. Others are directly converted by the class that uses them (although they should probably get an exporttoken as well - project anyone?)

Other times, the information is converted back to a String individually, generally in getPCCText() methods throughout the PCGen core. Again, there are probably better solutions to this (to provide more consistency in export and to reduce code quantity)

Discussion

As with any architecture, there are tradeoffs in having a plugin system. The first of these is in code association within the PCGen system. Due to the plugin nature (and the use of reflection) there are certain use-associations which cannot be made within an Integrated Development Environment (IDE) such as Eclipse. For example, it is impossible to find where a TemplateToken is constructed by automated search, as it is constructed by a Class.newInstance() call.

One quirk with the plugin system is also that it occasionally requires full rebuilds of the code in order to ensure the core code and the plugins are "in sync" on their functionality. This is reasonably rare, but is a result of the lack of a hard dependency tree in the code (really, the same problem IDEs have in determining usage)

There are also some great advantages to a plugin system.

By using reflection to perform the import of the classes and using reflection to inspect those classes, some associations can be made automatically, and do not require translation tables. By having all of the information directly within the Token Classes, a 'contract' to update multiple locations in the code (or parameter files) is avoided. There is also a minimal amount of indirection (the indirection introduced by TokenStore's Token map is very easy to understand).

The addition of a Token Class to the Plugin JAR will allow the new Token to be parsed. This makes adding new tags to the LST files to be reasonably painless (actually having it perform functions in the PCGen core is another matter :) )

Also, By keeping each Token in an individual class, this keeps the Token Classes very simple, which makes them easy to test, modify, and understand (as they are effectively atomic to the processing of a specific token)