io.guise.framework.AbstractGuiseSession Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of guise-framework Show documentation
Show all versions of guise-framework Show documentation
Guise™ Internet application framework.
/*
* Copyright © 2005-2008 GlobalMentor, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.guise.framework;
import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.net.URI;
import java.security.Principal;
import java.text.Collator;
import static java.nio.charset.StandardCharsets.*;
import static java.text.MessageFormat.*;
import java.util.*;
import static java.util.Collections.*;
import static java.util.Objects.*;
import static org.zalando.fauxpas.FauxPas.*;
import com.globalmentor.beans.*;
import com.globalmentor.collections.DecoratorReadWriteLockMap;
import com.globalmentor.collections.ReadWriteLockMap;
import com.globalmentor.io.BOMInputStreamReader;
import com.globalmentor.java.*;
import com.globalmentor.java.Objects;
import com.globalmentor.log.Log;
import com.globalmentor.net.*;
import com.globalmentor.util.*;
import io.csar.*;
import io.guise.framework.component.*;
import io.guise.framework.component.layout.Orientation;
import io.guise.framework.event.*;
import io.guise.framework.geometry.*;
import io.guise.framework.input.*;
import io.guise.framework.model.*;
import io.guise.framework.platform.Platform;
import io.guise.framework.prototype.*;
import io.guise.framework.style.*;
import io.guise.framework.theme.Theme;
import io.urf.model.UrfResourceDescription;
import static com.globalmentor.io.Filenames.*;
import static com.globalmentor.io.Writers.*;
import static com.globalmentor.java.CharSequences.*;
import static com.globalmentor.java.Characters.*;
import static com.globalmentor.java.Classes.*;
import static com.globalmentor.java.Conditions.unexpected;
import static com.globalmentor.net.URIs.*;
import static com.globalmentor.text.TextFormatter.*;
import static io.guise.framework.Resources.*;
import static io.guise.framework.theme.Theme.*;
/**
* An abstract implementation that keeps track of the components of a user session.
* @author Garret Wilson
*/
public abstract class AbstractGuiseSession extends BoundPropertyObject implements GuiseSession {
/** The manager of configurations for this session. */
private final ConcernRegistry configurationManager = new DefaultConcernRegistry();
/**
* Sets the given configurations, associating them with their respective classes.
* @param configurations The configurations to set.
*/
protected void setConfigurations(final Concern... configurations) {
configurationManager.registerConcerns(configurations);
}
/**
* Sets the given configuration, associating it with its class.
* @param The type of configuration being set.
* @param configuration The configuration to set.
* @return The configuration previously associated with the same class, if any.
* @throws NullPointerException if the given configuration is null
.
*/
protected Optional setConfiguration(final C configuration) {
return configurationManager.registerConcern(configuration);
}
/**
* Sets the given configuration.
* @param The type of configuration being set.
* @param configurationClass The class with which to associate the configuration.
* @param configuration The configuration to set.
* @return The configuration previously associated with the given class, if any.
*/
protected Optional setConfiguration(final Class configurationClass, final C configuration) {
return configurationManager.registerConcern(configurationClass, configuration);
}
@Override
public Optional findConcern(final Class configurationClass) {
return configurationManager.findConcern(configurationClass);
}
/**
* Removes a configuration of the given type. If no configuration is associated with the specified type, no action occurs.
* @param The type of configuration being removed.
* @param configurationClass The class with which the configuration is associated.
* @return The configuration previously associated with the given class, if any.
*/
protected Optional removeConfiguration(final Class configurationClass) {
return configurationManager.unregisterConcern(configurationClass);
}
/** The unique identifier of this session. */
private final UUID uuid;
@Override
public UUID getUUID() {
return uuid;
}
/** The Guise application to which this session belongs. */
private final GuiseApplication application;
@Override
public GuiseApplication getApplication() {
return application;
}
/** The writer for writing to the log file. */
private Writer logWriter;
@Override
public Writer getLogWriter() {
return logWriter;
}
@Override
public void setLogWriter(final Writer logWriter) {
this.logWriter = requireNonNull(logWriter, "Log writer cannot be null.");
}
/** The depiction base URI of the session. */
private URI depictionBaseURI;
@Override
public URI getDepictionRootURI() {
return depictionBaseURI;
}
@Override
public void setDepictionRootURI(final URI depictionBaseURI) {
if(!this.depictionBaseURI.equals(checkRoot(checkAbsolute(checkPlainURI(depictionBaseURI))))) { //if a new root URI is given
this.depictionBaseURI = depictionBaseURI; //save the new base URI
}
}
/**
* {@inheritDoc}
*
* This implementation delegates to {@link #getDepictionURI(URI, String...)}.
*
*/
@Override
public final URI getDepictionURI(final URIPath navigationPath, final String... suffixes) {
return getDepictionURI(navigationPath.toURI(), suffixes);
}
@Override
public URI getDepictionURI(final URI navigationURI, final String... suffixes) {
final GuiseApplication guiseApplication = getApplication(); //get the application
return guiseApplication.getDepictionURI(getDepictionRootURI(), dereferenceURI(navigationURI, suffixes)); //dereference the navigation URI and determine the depiction URI
}
/** The application frame, initialized during {@link #initialize()}. */
private ApplicationFrame applicationFrame = null;
@Override
public ApplicationFrame getApplicationFrame() {
if(applicationFrame == null) { //if this session has not yet been initialized
throw new IllegalStateException("Guise session " + this + " has not yet been initialized and therefore has no application frame.");
}
return applicationFrame; //return the application frame
}
/** The cache of components keyed to component destinations. */
private final Map destinationComponentMap = synchronizedMap(new HashMap());
/**
* The map of preference resource descriptions keyed to classes. This is a temporary implementation that will later be replaced with a backing store based
* upon the current principal.
*/
//TODO re-implement preferences
// private final ReadWriteLockMap, URFResource> classPreferencesMap = new DecoratorReadWriteLockMap, URFResource>(
// new HashMap, URFResource>());
//
// @Override
// public URFResource getPreferences(final Class objectClass) throws IOException {
// URFResource preferences = classPreferencesMap.get(requireNonNull(objectClass, "Class cannot be null.")); //get the preferences stored in the map
// if(preferences == null) { //if no preferences are stored in the map
// preferences = new DefaultURFResource(); //create a default set of preference properties
// /*TODO del if we decide to store resource copies; change map to concurrent map
// classPreferencesMap.writeLock().lock(); //get a write lock on the preferences map
// try
// {
// preferences=classPreferencesMap.get(objectClass); //try again to get the preferences stored in the map
// if(preferences==null) { //if preferences are still not stored in the map
// preferences=new DefaultRDFResource(); //create a default set of preference properties
// classPreferencesMap.put(objectClass, preferences); //store the preferences in the map
// }
// }
// finally
// {
// classPreferencesMap.writeLock().unlock(); //always release the write lock on the preferences map
// }
// */
// }
// return preferences; //return the preferences we found for this class
// }
//
// @Override
// public void setPreferences(final Class objectClass, final URFResource preferences) throws IOException {
// classPreferencesMap.put(requireNonNull(objectClass, "Class cannot be null."),
// new DefaultURFResource(requireNonNull(preferences, "Preferences cannot be null."))); //store a copy of the preferences in the map
// }
/** The platform on which Guise objects are depicted. */
private final Platform platform;
@Override
public Platform getPlatform() {
return platform;
}
/** The strategy for processing input, or null
if this session has no input strategy. */
private InputStrategy inputStrategy = null;
@Override
public InputStrategy getInputStrategy() {
return inputStrategy;
}
@Override
public void setInputStrategy(final InputStrategy newInputStrategy) {
if(!Objects.equals(inputStrategy, newInputStrategy)) { //if the value is really changing
final InputStrategy oldInputStrategy = inputStrategy; //get the current value
inputStrategy = newInputStrategy; //update the value
firePropertyChange(INPUT_STRATEGY_PROPERTY, oldInputStrategy, newInputStrategy);
}
}
/** The current session time zone. */
private TimeZone timeZone;
@Override
public TimeZone getTimeZone() {
return timeZone;
}
@Override
public void setTimeZone(final TimeZone newTimeZone) {
if(!timeZone.equals(newTimeZone)) { //if the value is really changing (compare their values, rather than identity)
final TimeZone oldTimeZone = timeZone; //get the old value
timeZone = requireNonNull(newTimeZone, "Guise session time zone cannot be null."); //actually change the value
firePropertyChange(TIME_ZONE_PROPERTY, oldTimeZone, newTimeZone); //indicate that the value changed
}
}
/** The current session locale. */
private Locale locale;
@Override
public Locale getLocale() {
return locale;
}
@Override
public void setLocale(final Locale newLocale) {
if(!Objects.equals(locale, newLocale)) { //if the value is really changing (compare their values, rather than identity) TODO locale should never be null, so no need to use Objects.equals()
final Locale oldLocale = locale; //get the old value
locale = requireNonNull(newLocale, "Guise session locale cannot be null."); //actually change the value
releaseResourceBundle(); //release the resource bundle, as the new locale may indicate that new resources should be used
firePropertyChange(LOCALE_PROPERTY, oldLocale, newLocale); //indicate that the value changed
setOrientation(Orientation.getOrientation(locale)); //update the orientation based upon the new locale
}
}
@Override
public Locale requestLocale(final List requestedLocales) {
final List supportedLocales = getApplication().getLocales(); //get the application's supported locales
//TODO del when works final Set supportedLocales=getApplication().getSupportedLocales(); //get the application's supported locales TODO maybe don't expose the whole set
for(final Locale requestedLocale : requestedLocales) { //for each requested locale
Locale acceptedLocale = null; //we'll determine if any variations of the requested locale is supported
if(supportedLocales.contains(requestedLocale)) { //if the application supports this local
acceptedLocale = requestedLocale; //accept this locale as-is
}
if(acceptedLocale == null && requestedLocale.getVariant().length() > 0) { //if the requested locale specifies a variant, see if there is a more general supported locale
final Locale languageCountryLocale = new Locale(requestedLocale.getLanguage(), requestedLocale.getCountry()); //create a more general locale with just the language and country
if(supportedLocales.contains(languageCountryLocale)) { //if the application supports this locale
acceptedLocale = languageCountryLocale; //accept this more general locale
}
}
if(acceptedLocale == null && requestedLocale.getCountry().length() > 0) { //if the requested locale specifies a country, see if there is an even more general supported locale
final Locale languageLocale = new Locale(requestedLocale.getLanguage()); //create a more general locale with just the language
if(supportedLocales.contains(languageLocale)) { //if the application supports this locale
acceptedLocale = languageLocale; //accept this more general locale
}
}
if(acceptedLocale != null) { //if we were able to find a supported locale
setLocale(acceptedLocale); //change to this locale
return acceptedLocale; //indicate which locale was accepted
}
}
return null; //indicate that the application supports none of the requested locales and none of their more general variations
}
/** The default internationalization orientation of components for this session. */
private Orientation orientation = Orientation.LEFT_TO_RIGHT_TOP_TO_BOTTOM;
@Override
public Orientation getOrientation() {
return orientation;
}
@Override
public void setOrientation(final Orientation newOrientation) {
if(!Objects.equals(orientation, newOrientation)) { //if the value is really changing
final Orientation oldOrientation = requireNonNull(orientation, "Orientation cannot be null"); //get the old value
orientation = newOrientation; //actually change the value
firePropertyChange(ORIENTATION_PROPERTY, oldOrientation, newOrientation); //indicate that the value changed
}
}
/** The lazily-created resource bundle used by this session. */
private ResourceBundle resourceBundle = null;
@Override
public ResourceBundle getResourceBundle() throws MissingResourceException {
if(resourceBundle == null) { //if the resource bundle has not yet been loaded
final Locale locale = getLocale(); //get the current locale
try {
resourceBundle = getApplication().loadResourceBundle(getTheme(), locale); //ask the application for the resource bundle based upon the locale
} catch(final IOException ioException) { //if there is an I/O error, convert it to a missing resource exception
throw (MissingResourceException)new MissingResourceException(ioException.getMessage(), null, null).initCause(ioException); //TODO check to see if null is OK for arguments here
}
}
return resourceBundle; //return the resource bundle
}
/**
* Unloads the current resource bundle so that the next call to {@link #getResourceBundle()} will load the resource bundle anew. This method also releases the
* current collator.
*/
protected void releaseResourceBundle() {
resourceBundle = null; //release our reference to the resource bundle
collator = null; //release the current collator
}
/**
* The property value change listener that, in response to a change in value, releases the resource bundle.
* @see #releaseResourceBundle()
*/
private final GenericPropertyChangeListener resourceBundleReleasePropertyValueChangeListener = new AbstractGenericPropertyChangeListener() {
@Override
public void propertyChange(final GenericPropertyChangeEvent propertyChangeEvent) {
releaseResourceBundle(); //release the resource bundle, as the new locale may indicate that new resources should be used
}
};
@Override
@SuppressWarnings("unchecked")
public T getResource(final String resourceKey) throws MissingResourceException {
return (T)getResourceBundle().getObject(resourceKey); //retrieve an object from the resource bundle
}
@Override
@SuppressWarnings("unchecked")
public T getResource(final String resourceKey, final T defaultValue) throws MissingResourceException {
try {
return (T)getResource(resourceKey); //try to load the string from the resources
} catch(final MissingResourceException missingResourceException) { //if no such resource is available
return defaultValue; //return the specified default value
}
}
@Override
public String getStringResource(final String resourceKey) throws MissingResourceException { //TODO convert the resource to a string using toString()
try {
return getResource(resourceKey); //retrieve a string from the resource bundle
} catch(final MissingResourceException missingResourceException) { //if the resource does not exist
if(isPath(resourceKey) && !isPathAbsolute(resourceKey)) { //if the resource key is a relative path
final String applicationResourcePath = getApplication().getLocaleResourcePath(resourceKey, getLocale()); //try to get a locale-sensitive path to the resource
if(applicationResourcePath != null) { //if there is a path to the resource
try (final InputStream inputStream = getApplication().getResourceInputStream(applicationResourcePath)) { //get a stream to the resource
if(inputStream != null) { //if we got a stream to the resource (we always should, as we already checked to see which path represents an existing resource)
final StringWriter stringWriter = new StringWriter(); //create a new string writer to receive the resource contents
//TODO do better checking of XML encoding type by prereading
final Reader resourceReader = new BOMInputStreamReader(new BufferedInputStream(inputStream), UTF_8); //get an input reader to the file, defaulting to UTF-8 if we don't know its encoding
write(resourceReader, stringWriter); //copy the resource to the string
return stringWriter.toString(); //return the string read from the resource
}
} catch(final IOException ioException) { //if there is an I/O error, convert it to a missing resource exception
throw (MissingResourceException)new MissingResourceException(ioException.getMessage(), missingResourceException.getClassName(),
missingResourceException.getKey()).initCause(ioException);
}
}
}
throw missingResourceException; //if we couldn't find an application resource, throw the original missing resource exception
}
}
@Override
public String getStringResource(final String resourceKey, final String defaultValue) throws MissingResourceException {
try {
return getStringResource(resourceKey); //try to load the string from the resources
} catch(final MissingResourceException missingResourceException) { //if no such resource is available
return defaultValue; //return the specified default value
}
}
@Override
public Boolean getBooleanResource(final String resourceKey) throws MissingResourceException {
final Object resource = getResource(resourceKey); //retrieve a resource from the resource bundle
if(resource instanceof String) { //if the resource is a string
return Boolean.valueOf(dereferenceString((String)resource)); //get the Boolean value of the resource string
} else { //if the resource is not a string, assume it is a Boolean
return (Boolean)resource; //return the resource as a Boolean object, throwing a ClassCastException if it isn't an instance of Boolean
}
}
@Override
public Boolean getBooleanResource(final String resourceKey, final Boolean defaultValue) throws MissingResourceException {
try {
return getBooleanResource(resourceKey); //try to load the Boolean from the resources
} catch(final MissingResourceException missingResourceException) { //if no such resource is available
return defaultValue; //return the specified default value
}
}
@Override
public Color getColorResource(final String resourceKey) throws MissingResourceException {
final Object resource = getResource(resourceKey); //retrieve a resource from the resource bundle
if(resource instanceof String) { //if the resource is a string
return AbstractModeledColor.valueOf(dereferenceString((String)resource)); //create a color from the resolved string
} else { //if the resource is not a string, assume it is a color
return (Color)resource; //return the resource as a color object, throwing a ClassCastException if it isn't an instance of Color
}
}
@Override
public Color getColorResource(final String resourceKey, final Color defaultValue) throws MissingResourceException {
try {
return getColorResource(resourceKey); //try to load the color from the resources
} catch(final MissingResourceException missingResourceException) { //if no such resource is available
return defaultValue; //return the specified default value
}
}
@Override
public Integer getIntegerResource(final String resourceKey) throws MissingResourceException {
final Object resource = getResource(resourceKey); //retrieve a resource from the resource bundle
if(resource instanceof String) { //if the resource is a string
return Integer.valueOf(dereferenceString((String)resource)); //get the Integer value of the resource string
} else { //if the resource is not a string, assume it is an Integer
return (Integer)resource; //return the resource as an Integer object, throwing a ClassCastException if it isn't an instance of Integer
}
}
@Override
public Integer getIntegerResource(final String resourceKey, final Integer defaultValue) throws MissingResourceException {
try {
return getIntegerResource(resourceKey); //try to load the Integer from the resources
} catch(final MissingResourceException missingResourceException) { //if no such resource is available
return defaultValue; //return the specified default value
}
}
@Override
public URI getURIResource(final String resourceKey) throws MissingResourceException {
final Object resource = getResource(resourceKey); //retrieve a resource from the resource bundle
if(resource instanceof String) { //if the resource is a string
return URI.create(dereferenceString((String)resource)); //create a URI from the resource string
} else { //if the resource is not a string, assume it is a URI
return (URI)resource; //return the resource as a URI object, throwing a ClassCastException if it isn't an instance of URI
}
}
@Override
public URI getURIResource(final String resourceKey, final URI defaultValue) throws MissingResourceException {
try {
return getURIResource(resourceKey); //try to load the URI from the resources
} catch(final MissingResourceException missingResourceException) { //if no such resource is available
return defaultValue; //return the specified default value
}
}
/** The lazily-created collator for the current locale, or null
if no collator has been created for the current locale. */
private Collator collator = null;
@Override
public Collator getCollatorInstance() {
Collator collator = this.collator; //get the current collator
if(collator == null) { //if no collator has yet been created for the current locale
collator = Collator.getInstance(getLocale()); //get a collator for the current locale
collator.setStrength(Collator.PRIMARY); //sort according to primary differences---ignore accents and case differences
collator.setDecomposition(Collator.FULL_DECOMPOSITION); //fully decompose Unicode characters to get the best comparison
this.collator = collator; //cache the collator for future requests
}
return collator; //return the collator we found
}
/** The current principal (e.g. logged-in user), or null
if there is no principal authenticated for this session. */
private Principal principal;
@Override
public Principal getPrincipal() {
return principal;
}
@Override
public void setPrincipal(final Principal newPrincipal) {
if(!Objects.equals(principal, newPrincipal)) { //if the value is really changing (compare their values, rather than identity)
final Principal oldPrincipal = principal; //get the old value
principal = newPrincipal; //actually change the value
firePropertyChange(PRINCIPAL_PROPERTY, oldPrincipal, newPrincipal); //indicate that the value changed
}
}
/** The current session theme, or null
if the theme has not been loaded. */
private Theme theme = null;
@Override
public Theme getTheme() throws IOException {
if(theme == null) { //if there is no theme (this race condition is more or less benign, and will only result in the theme being loaded more than once initially)
theme = getApplication().loadTheme(getThemeURI()); //ask the application to load the theme
}
return theme;
}
/** The URI of the session theme, to be resolved against the application base path. */
private URI themeURI;
@Override
public URI getThemeURI() {
return themeURI;
}
@Override
public void setThemeURI(final URI newThemeURI) {
if(!Objects.equals(themeURI, newThemeURI)) { //if the value is really changing
final URI oldThemeURI = themeURI; //get the old value
themeURI = requireNonNull(newThemeURI, "Theme URI cannot be null."); //actually change the value
theme = null; //release our reference to the current theme
firePropertyChange(THEME_URI_PROPERTY, oldThemeURI, newThemeURI); //indicate that the value changed
}
}
/** The action prototype for presenting application information. */
private final ActionPrototype aboutApplicationActionPrototype;
@Override
public ActionPrototype getAboutApplicationActionPrototype() {
return aboutApplicationActionPrototype;
}
/**
* Application and platform constructor. The session local will initially be set to the locale of the associated Guise application. No operation must be
* performed inside the constructor that would require the presence of the Guise session within this thread group.
* @param application The Guise application to which this session belongs.
* @param platform The platform on which this session's objects are depicted.
* @throws NullPointerException if the given application and/or platform is null
.
*/
public AbstractGuiseSession(final GuiseApplication application, final Platform platform) {
this.uuid = UUID.randomUUID(); //create a UUID for the session
this.application = requireNonNull(application, "Application cannot be null."); //save the application
this.depictionBaseURI = resolve(application.getContainer().getBaseURI(), application.getBasePath().toURI()); //default to a base URI calculated from the application base path resolved to the container's base URI TODO fix to convert from navigation to depiction path
this.platform = requireNonNull(platform, "Platform cannot be null."); //save the platform
this.themeURI = application.getThemeURI(); //default to the application theme
this.locale = application.getLocales().get(0); //default to the first application locale
this.timeZone = TimeZone.getDefault(); //default to the default time zone
this.orientation = Orientation.getOrientation(locale); //set the orientation default based upon the locale
logWriter = new OutputStreamWriter(System.err); //default to logging to the error output; this will be replaced after the session is created
//about action prototype
aboutApplicationActionPrototype = new AbstractActionPrototype(LABEL_ABOUT_X + createStringValueReference(APPLICATION_NAME), GLYPH_ABOUT) {
@Override
protected void action(final int force, final int option) {
final AboutPanel aboutPanel = new AboutPanel(); //create a new about panel
aboutPanel.setNameLabel(APPLICATION_NAME);
aboutPanel.setVersionLabel(LABEL_VERSION + ' ' + APPLICATION_VERSION);
aboutPanel.setCopyrightLabel(APPLICATION_COPYRIGHT);
final Frame aboutFrame = new NotificationOptionDialogFrame(aboutPanel, Notification.Option.OK); //create an about frame
aboutFrame.setLabel(LABEL_ABOUT + ' ' + APPLICATION_NAME); //set the title
aboutFrame.open(true); //show the about dialog
}
};
}
@Override
public Component getDestinationComponent(final ComponentDestination destination) {
Component component; //we'll store the component here, either a cached component or a created component
synchronized(destinationComponentMap) { //don't allow the map to be modified while we access it
component = destinationComponentMap.get(destination); //get cached component, if any
if(component == null) { //if no component is cached
//TODO maybe verify that this destination is actually associated with the navigation path for this application final Destination destination=getApplication().getDestination(path); //get the destination for this path
component = createComponent(destination.getComponentClass()); //create the component
destinationComponentMap.put(destination, component); //bind the component to the path, caching it for next time
}
}
return component; //return the panel, or null if we couldn't find a panel
}
@Override
public Component releaseDestinationComponent(final ComponentDestination destination) {
return destinationComponentMap.remove(destination); //uncache the component
}
@Override
public Component getNavigationComponent(final URIPath path) {
final Destination destination = getApplication().getDestination(path).orElse(null); //get the destination associated with the given path TODO propagate use of Optional
if(!(destination instanceof ComponentDestination)) { //if the destination is not a component destination
throw new IllegalArgumentException("Navigation path " + path + " does not designate a component destination.");
}
return getDestinationComponent((ComponentDestination)destination); //return the component
}
@Override
public Optional getNavigationDescription(final URIPath navigationPath, final Bookmark bookmark) throws IOException {
//delegate to the destination, if any, associated with the path
return getApplication().getDestination(navigationPath)
.flatMap(throwingFunction(destination -> destination.getDescription(this, navigationPath, bookmark, null)));
}
@Override
public Optional getNavigationDescription() throws IOException {
return getNavigationDescription(getNavigationPath(), getBookmark());
}
/**
* Creates the component for the given class.
* @param componentClass The class representing the component to create.
* @return The created component.
* @throws IllegalStateException if the component class does not provide a default constructor, is an interface, is abstract, or throws an exception during
* instantiation.
*/
protected Component createComponent(final Class componentClass) {
Component component; //we'll store the component here
try {
component = componentClass.newInstance(); //create a new instance of the component
} catch(final IllegalAccessException illegalAccessException) { //if the constructor is not visible
throw new IllegalStateException(illegalAccessException);
} catch(final InstantiationException instantiationException) { //if the class is an interface or is abstract
throw new IllegalStateException(instantiationException);
}
initializeComponent(component); //initialize the component from a TURF description, if possible
return component; //return the component
}
@Override
public void initializeComponent(final Component component) {
/*TODO re-implement component descriptions
final Class componentClass = component.getClass(); //get the class of the component
final String descriptionFilename = addExtension(getLocalName(componentClass), TURF.NAME_EXTENSION); //create a name in the form ClassName.turf
//TODO del Log.trace("Trying to load description file:", descriptionFilename);
final InputStream descriptionInputStream = componentClass.getResourceAsStream(descriptionFilename); //get an input stream to the description file
if(descriptionInputStream != null) { //if we have a description file
try {
try {
initializeComponent(component, descriptionInputStream); //initialize the component from the description, calling the initialize() method in the process
} finally {
descriptionInputStream.close(); //always close the description input stream for good measure
}
} catch(final IOException ioException) { //if there is an I/O exception
throw new IllegalStateException(ioException); //TODO fix better
} catch(final DataException dataException) {
throw new IllegalStateException(dataException); //TODO fix better
} catch(final InvocationTargetException invocationTargetException) {
throw new IllegalStateException(invocationTargetException); //TODO fix better
}
} else { //if there is no description file
component.initialize(); //call the initialize() method manually
}
*/
}
/**
* {@inheritDoc}
*
* This implementation calls {@link #initializeComponent(Component, InputStream)}.
*
*/
@Override
public void initializeComponentFromResource(final Component component, final String resourceKey) throws DataException, InvocationTargetException {
final String descriptionResource = getStringResource(resourceKey); //get the description resource
try {
final InputStream descriptionInputStream = new ByteArrayInputStream(descriptionResource.getBytes(UTF_8)); //convert the string to bytes and create an input string to the array of bytes TODO verify the encoding somehow
initializeComponent(component, descriptionInputStream); //initialize the component from the description resource
} catch(final UnsupportedEncodingException unsupportedEncodingException) { //UTF-8 should always be supported
throw new AssertionError(unsupportedEncodingException);
} catch(final IOException ioException) { //we should never have an I/O exception reading from a byte array input stream
throw new AssertionError(ioException);
}
}
@Override
public void initializeComponent(final Component component, final InputStream descriptionInputStream)
throws IOException, DataException, InvocationTargetException {
/*TODO re-implement component descriptions
final URI BASE_URI = URI.create("guise:/"); //TODO fix
final URF urf = AbstractTURFIO.readTURF(new URF(), descriptionInputStream, BASE_URI); //read TURF from the input stream
final URI componentResourceTypeURI = createJavaURI(component.getClass()); //create a new URI that indicates the type of the resource description we expect
final URFResource componentResource = urf.getResourceByTypeURI(componentResourceTypeURI); //try to locate the description of the given component
if(componentResource != null) { //if there is a resource description of a matching type
final PLOOPURFProcessor ploopProcessor = new PLOOPURFProcessor(); //create a new PLOOP processor
ploopProcessor.setObjectProperties(component, componentResource); //initialize the component from this resource
component.initialize(); //initialize the component
final List