/*******************************************************************************
* Copyright (c) 2007, 2015 Innoopract Informationssysteme GmbH and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Innoopract Informationssysteme GmbH - initial API and implementation
* EclipseSource - ongoing implementation
* Ralf Zahn (ARS) - browser history support (Bug 283291)
* Frank Appel - replaced singletons and static fields (Bug 337787)
******************************************************************************/
package org.eclipse.rap.rwt;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.rap.rwt.application.Application;
import org.eclipse.rap.rwt.client.Client;
import org.eclipse.rap.rwt.internal.lifecycle.CurrentPhase;
import org.eclipse.rap.rwt.internal.lifecycle.LifeCycle;
import org.eclipse.rap.rwt.internal.lifecycle.LifeCycleUtil;
import org.eclipse.rap.rwt.internal.service.ContextProvider;
import org.eclipse.rap.rwt.internal.service.ServletLog;
import org.eclipse.rap.rwt.internal.util.ClassUtil;
import org.eclipse.rap.rwt.internal.util.ParamCheck;
import org.eclipse.rap.rwt.service.ApplicationContext;
import org.eclipse.rap.rwt.service.ResourceManager;
import org.eclipse.rap.rwt.service.ServiceManager;
import org.eclipse.rap.rwt.service.SettingStore;
import org.eclipse.rap.rwt.service.UISession;
import org.eclipse.swt.SWT;
import org.eclipse.swt.internal.widgets.IDisplayAdapter;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Widget;
/**
* This class provides access to those parts of RWT which are not covered by the SWT API. For
* example, it provides access to the current UI session and the request.
*
* @since 2.0
* @see UISession
* @see ApplicationContext
* @see ResourceManager
* @see HttpServletRequest
* @see HttpServletResponse
*/
public final class RWT {
/**
* <p>This utility class helps to provide a similar approach for compile safe
* native language support than {@link org.eclipse.osgi.util.NLS NLS} does.
* We can not use the original approach though, due to the nature of
* server side environments, that have to deal with different locales
* per user session or even requests.</p>
*
* <p>
* Usage:
* <pre>
* public class FooMessages {
* private static final String BUNDLE_NAME = "foo.bar.messages";
*
* public String MyMessage;
*
* public static FooMessages get() {
* return ( FootMessages )RWT.NLS.getISO8859_1Encoded( BUNDLE_NAME, FooMessages.class );
* }
* }
* </pre>
*
* BUNDLE_NAME contains the name of a properties file (without file extension)
* that follows the conventions of standard {@link ResourceBundle} property
* files. For each field (in the example 'MyMessage') there has to be a
* key entry in the localization property file. Use the
* <code>FooMessages</code> like this in the application code:
*
* <pre>
* Label label = ...;
* label.setText( FooMessages.get().MyMessage );
* </pre>
* </p>
*/
@SuppressWarnings("javadoc")
public static final class NLS {
private final static Map<ResourceBundle,Object> map = new HashMap<>();
/**
* Returns a NLS object for the given resource bundle and type. See
* class description for usage information.
* The resource bundles read by this method have to be ISO 8859-1 encoded.
* This is according to the {@link java.util.Properties Properties} file
* specification.
*
* @param bundleName the resource bundle to load.
* @param clazz the class of the NLS object to load.
*/
public static <T> T getISO8859_1Encoded( String bundleName, Class<T> clazz ) {
ClassLoader loader = clazz.getClassLoader();
ResourceBundle bundle = ResourceBundle.getBundle( bundleName, getLocale(), loader );
return internalGet( bundle, clazz );
}
/**
* Returns a NLS object for the given resource bundle and type. See
* class description for usage information.
* The resource bundles read by this method have to be UTF-8 encoded. Note
* that this is not according to the {@link java.util.Properties Properties}
* file specification and meant for a more convenient use.
*
* @param bundleName the resource bundle to load.
* @param clazz the class of the NLS object to load.
*/
public static <T> T getUTF8Encoded( String bundleName, Class<T> clazz ) {
ClassLoader loader = clazz.getClassLoader();
ResourceBundle bundle = Utf8ResourceBundle.getBundle( bundleName, getLocale(), loader );
return internalGet( bundle, clazz );
}
@SuppressWarnings( "unchecked" )
private static <T> T internalGet( ResourceBundle bundle, Class<T> clazz ) {
T result;
synchronized( map ) {
result = ( T )map.get( bundle );
if( result == null ) {
result = ClassUtil.newInstance( clazz );
Field[] fields = clazz.getDeclaredFields();
for( int i = 0; i < fields.length; i++ ) {
String fieldName = fields[ i ].getName();
try {
if( String.class.isAssignableFrom( fields[ i ].getType() )
&& Modifier.isPublic( fields[ i ].getModifiers() )
&& !Modifier.isStatic( fields[ i ].getModifiers() ) )
{
try {
String value = bundle.getString( fieldName );
if( value != null ) {
fields[ i ].setAccessible( true );
fields[ i ].set( result, value );
}
} catch( MissingResourceException mre ) {
fields[ i ].setAccessible( true );
fields[ i ].set( result, "" );
throw mre;
}
}
} catch( Exception ex ) {
String qualifiedName = clazz.getName() + "#" + fieldName;
ServletLog.log( "Failed to load localized message for: " + qualifiedName, ex );
}
}
map.put( bundle, result );
}
}
return result;
}
}
/**
* The property to use in <code>Display.setData()</code> in order to activate global key events
* for certain key sequences. The value for this property has to be an array of Strings, each
* representing a key sequence. When this property is set on the display, the client will be
* instructed to issue events for the given key sequences. These key events can be captured using
* <code>Display.addFilter()</code>.
* <p>
* The property can also be used in <code>Control.setData()</code>. In this case, a key listener
* that is attached to that control will only receive events for the specified key sequences.
* Control without active keys set will issue events for all key strokes.
* </p>
* <p>
* Valid strings for key sequences consist of one key and any number of modifier keys,
* separated by <code>+</code>. Keys can be identified by their upper case character or by any
* of the keywords below. Special characters (not a letter or digit) should not be combined with
* any modifiers, and will issue events regardless of pressed modifiers.
* </p>
* <p>
* The following keywords can be used to refer to special keys:
* <code>BACKSPACE</code>, <code>TAB</code>, <code>RETURN</code>,
* <code>ENTER</code>, <code>ESCAPE</code>, <code>SPACE</code>, <code>PAGE_UP</code>,
* <code>PAGE_DOWN</code>, <code>END</code>, <code>HOME</code>, <code>ARROW_LEFT</code>,
* <code>ARROW_UP</code>, <code>ARROW_RIGHT</code>, <code>ARROW_DOWN</code>, <code>INSERT</code>,
* <code>DELETE</code>, <code>F1</code>, <code>F2</code>, <code>F3</code>, <code>F4</code>,
* <code>F5</code>, <code>F6</code>, <code>F7</code>, <code>F8</code>, <code>F9</code>,
* <code>F10</code>, <code>F11</code>, <code>F12</code>, Valid modifier keys are
* <code>SHIFT</code>, <code>ALT</code>, and <code>CTRL</code>.
* </p>
* Examples: <code>"A"</code>, <code>"#"</code>, <code>"F12"</code>,
* <code>"CTRL+1"</code>, <code>"ALT+ARROW_DOWN"</code>,
* <code>"ALT+SHIFT+X"</code>.
* <p>
* </p>
* <p>
* Example code for implementing a key binding: <code><pre>
* display.setData( RWT.ACTIVE_KEYS, new String[] { "CTRL+1", "CTRL+2" } );
* display.addFilter( SWT.KeyDown, new Listener() {
* public void handleEvent( Event event ) {
* boolean ctrlPressed = ( event.stateMask & SWT.Ctrl ) != 0;
* if( ctrlPressed && event.character == '1' ) {
* // handle Ctrl+1
* }
* }
* } );
* </pre></code>
* </p>
*
* @see Display#setData(String,Object)
* @see Display#addFilter(int, Listener)
* @see RWT#CANCEL_KEYS
*/
public static final String ACTIVE_KEYS = "org.eclipse.rap.rwt.activeKeys";
/**
* The property to use in <code>Display.setData()</code> in order to always cancel the client's
* default operation associated with certain key sequences. It allows the same values as
* {@link RWT#ACTIVE_KEYS}. If a key sequences is given in {@link RWT#CANCEL_KEYS} as well as
* in {@link RWT#ACTIVE_KEYS}, it will cancel its default operation, but still issue the event.
* <p>
* The property can also be used in <code>Control.setData()</code>. In this case, the associated
* default operation will only be cancelled if the control is focused.
* </p>
*
* <p>
* Depending on the client, there may be certain keys that cannot be cancelled.
* </p>
*
* @see Display#setData(String,Object)
* @see RWT#ACTIVE_KEYS
*/
public static final String CANCEL_KEYS = "org.eclipse.rap.rwt.cancelKeys";
/**
* The property to use in <code>Display.setData()</code> in order to set the key combination for
* mnemonics activation. The value for this property has to be a String.
* <p>
* Valid string for key sequence consist of any number of modifier keys, separated by
* <code>+</code>.
* </p>
* <p>
* Mnemonics are currently supported by <code>MenuItem</code>, <code>Button</code>,
* <code>Label</code>, <code>CLabel</code>, <code>Group</code>, <code>ToolItem</code>,
* <code>TabItem</code> and <code>CTabItem</code>. Mnemonics are not supported on a widgets
* with enabled markup.
* </p>
* <p>
* Example code:<code><pre>
* display.setData( RWT.MNEMONIC_ACTIVATOR, "ALT+CTRL" );
* </pre></code>
* </p>
*
* @see Display#setData(String,Object)
* @since 2.1
*/
public static final String MNEMONIC_ACTIVATOR = "org.eclipse.rap.rwt.mnemonicActivator";
/**
* The property to use in <code>Control.setData()</code> in order to set a custom item height.
* The custom item height must be specified as an <code>Integer</code> and passed to
* <code>setData()</code> with this constant as the key.
* <p>
* For example:
* <code>table.setData( RWT.CUSTOM_ITEM_HEIGHT, Integer.valueOf( 45 ) );</code>
* </p>
* <p><b>Used By:</b><ul>
* <li><code>Table</code></li>
* <li><code>Tree</code></li>
* <li><code>List</code></li>
* </ul></p>
*
* @see Control#setData(String,Object)
*/
public static final String CUSTOM_ITEM_HEIGHT = "org.eclipse.rap.rwt.customItemHeight";
/**
* Controls the number of preloaded items outside (above and below) visible area of virtual
* <code>Tree</code> or <code>Table</code>. The preloaded items must be specified as an
* <code>Integer</code> and passed to <code>setData()</code> with this constant as the key.
* <p>
* For example: <code>table.setData( RWT.PRELOADED_ITEMS, Integer.valueOf( 10 ) );</code>
* </p>
* <p>
* <b>Used By:</b>
* <ul>
* <li><code>Table</code></li>
* <li><code>Tree</code></li>
* </ul>
* </p>
*
* @see Control#setData(String,Object)
* @since 2.2
*/
public static final String PRELOADED_ITEMS = "org.eclipse.rap.rwt.preloadedItems";
/**
* Controls whether the use of <em>markup</em> in text is enabled. To enable
* markup in text, this constant must be passed to <code>setData()</code> with
* a value of <code>Boolean.TRUE</code>. The call to <code>setData()</code>
* must be placed directly after the control is created. Once the markup in text
* is enabled it's not possible to disable it.
* <p>
* For example:
* <code><pre>
* Table table = new Table( parent, SWT.NONE );
* table.setData( RWT.MARKUP_ENABLED, Boolean.TRUE );
* </pre></code>
* </p>
* <p>
* When markup is enabled, certain XHTML tags can be used in the text property
* of the respective widget. Specifying an unsupported element will lead to an
* {@link IllegalArgumentException} when setting the text. The following table
* lists the currently supported tags:
* <dl>
* <dt>{@literal <b>text</b>}</dt>
* <dd>renders its content in bold font style</dd>
* <dt>{@literal <i>text</i>}</dt>
* <dd>renders its content in italic font style</dd>
* <dt>{@literal <br/>}</dt>
* <dd>inserts a line break</dd>
* <dt>{@literal <sub>}</dt>
* <dd>renders its content as subscript</dd>
* <dt>{@literal <sup>}</dt>
* <dd>renders its content as superscript</dd>
* <dt>{@literal <big>}</dt>
* <dd>renders its content with bigger font size</dd>
* <dt>{@literal <small>}</dt>
* <dd>renders its content with smaller font size</dd>
* <dt>{@literal <del>}</dt>
* <dd>renders its content as deleted text</dd>
* <dt>{@literal <ins>}</dt>
* <dd>renders its content as inserted text</dd>
* <dt>{@literal <em>}</dt>
* <dd>renders its content as emphasized text</dd>
* <dt>{@literal <strong>}</dt>
* <dd>renders its content as strong emphasized text</dd>
* <dt>{@literal <dfn>}</dt>
* <dd>renders its content as instance definition</dd>
* <dt>{@literal <code>}</dt>
* <dd>renders its content as computer code fragment</dd>
* <dt>{@literal <samp>}</dt>
* <dd>renders its content as sample program output</dd>
* <dt>{@literal <kbd>}</dt>
* <dd>renders its content as text to be entered by the user</dd>
* <dt>{@literal <var>}</dt>
* <dd>renders its content as instance of a variable or program argument</dd>
* <dt>{@literal <cite>}</dt>
* <dd>renders its content as citation</dd>
* <dt>{@literal <q>}</dt>
* <dd>renders its content as short inline quotation</dd>
* <dt>{@literal <abbr>}</dt>
* <dd>renders its content as abbreviation</dd>
* <dt>{@literal <span>}</dt>
* <dd>generic style container</dd>
* <dt>{@literal <img>}</dt>
* <dd>renders an image</dd>
* <dt>{@literal <a>}</dt>
* <dd>renders a hyperlink</dd>
* </dl>
* The visual representation of the above tags can be specified in a <code>style</code>
* attribute.
* </p>
* <p>
* <b>Used By:</b>
* <ul>
* <li><code>Table</code></li>
* <li><code>Tree</code></li>
* <li><code>Grid</code></li>
* <li><code>List</code></li>
* <li><code>Label</code></li>
* <li><code>CLabel</code></li>
* <li><code>ToolTip</code></li>
* </ul>
* </p>
*
* @see Control#setData(String,Object)
* @see RWT#HYPERLINK
*/
public static final String MARKUP_ENABLED = "org.eclipse.rap.rwt.markupEnabled";
/**
* Controls whether the use of <em>markup</em> in tooltip text is enabled. To enable
* markup in tooltip text, this constant must be passed to <code>setData()</code> with
* a value of <code>Boolean.TRUE</code>. The call to <code>setData()</code>
* must be placed directly after the control is created. Once, the markup in tooltip text
* is enabled it's not possible to disable it.
*
* @see Control#setData(String,Object)
* @see RWT#MARKUP_ENABLED
* @since 2.2
*/
public static final String TOOLTIP_MARKUP_ENABLED = "org.eclipse.rap.rwt.tooltipMarkupEnabled";
/**
* Controls the number of fixed columns. This constant must be passed to <code>setData()</code>
* together with an <code>Integer</code> object. The given number of columns, starting
* with the current leftmost one, will not scroll horizontally. The call to <code>setData()</code>
* must be placed directly after the control is created.
* <p>
* For example:
* <code><pre>
* Table table = new Table( parent, SWT.NONE );
* table.setData( RWT.FIXED_COLUMNS, Integer.valueOf( 2 ) );
* </pre></code>
* </p>
* <b>Used By:</b>
* <ul>
* <li><code>Table</code></li>
* <li><code>Tree</code></li>
* </ul>
* </p>
*
* @see Control#setData(String,Object)
*/
public static final String FIXED_COLUMNS = "org.eclipse.rap.rwt.fixedColumns";
/**
* Controls the text shown as a badge. This constant must be passed to <code>setData()</code>
* together with an <code>String</code> object.
* <p>
* For example:
* <code><pre>
* TabItem item = new TabItem( folder, SWT.NONE );
* item.setData( RWT.BADGE, "23" );
* </pre></code>
* </p>
* <b>Used By:</b>
* <ul>
* <li><code>TabItem</code></li>
* </ul>
* </p>
*
* @see Control#setData(String,Object)
* @since 3.0
*/
public static final String BADGE = "org.eclipse.rap.rwt.badge";
/**
* The ID of the default theme. The default theme is the active theme if no
* custom theme has been specified. This ID can be used to register theme
* contributions to the default theme.
*
* @see Application#addStyleSheet(String, String)
*/
public static final String DEFAULT_THEME_ID = "org.eclipse.rap.rwt.theme.Default";
/**
* Used to mark a widget as belonging to a custom variant in order to apply a
* different theming to it. A custom variant can be applied to any widget like this:
*
* <pre>
* button.setData( RWT.CUSTOM_VARIANT, "mybutton" );
* </pre>
*
* For more information on custom variants, see the RAP help on theming.
*
* @see Widget#setData(String,Object)
* @since 2.0
*/
public static final String CUSTOM_VARIANT = "org.eclipse.rap.rwt.customVariant";
/**
* Used to apply a row template to a control. Row templates replace the column layout model of a
* Tree or a Table with a custom presentation defined by an instance of
* {@link org.eclipse.rap.rwt.template.Template}. A template cell will display the content of an
* item's column when its <em>bindingIndex</em> is set to the corresponding column index.
* <p>
* To apply a row template on a control, use the control's <code>setData()</code> method with this
* constant as key:
* </p>
*
* <pre>
* Template template = new Template();
* // add cells to this template
* new TextCell(template).setBindingIndex(0).setTop(10).setLeft(20) ...;
* ...
* Table table = new Table(parent, SWT.FULL_SELECTION);
* // Add as many columns as needed to add multiple texts/images to items
* new TableColumn();
* ...
* table.setData(RWT.ROW_TEMPLATE, template);
* </pre>
* <p>
* The call to <code>setData()</code> must be placed directly after the control's creation. Once a
* template is applied to a control, the control will not be affected by changes to the template.
* </p>
* <p>
* Note that TableColumn/TreeColumn instances must be created in order to support multiple item
* texts/images. If the <code>SWT.FULL_SELECTION</code> style flag is not set, no selection will
* be displayed.
* </p>
* <p>
* <b>Supported by:</b>
* <ul>
* <li><code>Table</code></li>
* <li><code>Tree</code></li>
* <li><code>Grid</code></li>
* </ul>
* </p>
*
* @see org.eclipse.swt.widgets.Control#setData(String,Object)
* @see org.eclipse.rap.rwt.template.Template
* @since 2.2
*/
public static final String ROW_TEMPLATE = "org.eclipse.rap.rwt.rowTemplate";
/**
* Used as <em>detail</em> information on a selection event to indicate that a hyperlink (anchor
* tag) in a markup text was selected instead of the widget that contains the markup. To enable
* selection events on markup hyperlinks, the <code>a</code> element must have it's
* <code>target</code> property set to “<code>_rwt</code>”.
*
* <p>
* <b>Supported by:</b>
* <ul>
* <li><code>Table</code></li>
* <li><code>Tree</code></li>
* <li><code>Grid</code></li>
* <li><code>List</code></li>
* </ul>
* </p>
*
* @see RWT#MARKUP_ENABLED
* @see org.eclipse.swt.events.SelectionEvent#detail
* @since 2.1
*/
public static final int HYPERLINK = 1 << 26;
/**
* Used as as <em>detail</em> information on a selection event to indicate that a selectable
* template cell was selected instead of the widget that contains the cell.
*
* @see org.eclipse.rap.rwt.template.Cell#setSelectable(boolean)
* @see org.eclipse.swt.events.SelectionEvent#detail
* @since 2.2
*/
public static final int CELL = 1 << 27;
/**
* Returns the instance of the resource manager for the current application context. This is a
* shortcut for <code>RWT.getApplicationContext().getResourceManager()</code>.
*
* @return the resource manager for the current application context
* @see ApplicationContext#getResourceManager()
*/
public static ResourceManager getResourceManager() {
return getApplicationContext().getResourceManager();
}
/**
* Returns the instance of the service manager for the current application context. This is a
* shortcut for <code>RWT.getApplicationContext().getServiceManager()</code>.
*
* @return the service manager instance for the current application context
* @see ApplicationContext#getServiceManager()
*/
public static ServiceManager getServiceManager() {
return getApplicationContext().getServiceManager();
}
/**
* Returns the setting store instance for the current UI session. The setting store is a
* persistent store for user-specific data.
*
* @return the setting store for the current session, never <code>null</code>
*/
public static SettingStore getSettingStore() {
return ContextProvider.getApplicationContext().getSettingStoreManager().getStore();
}
/**
* Returns the current UI session. This method must be executed from the UI thread.
*
* @return the current UI session instance, never <code>null</code>
* @throws IllegalStateException when called outside of the UI thread
*/
public static UISession getUISession() {
checkContext();
return ContextProvider.getUISession();
}
/**
* Returns the UI session that is associated with the given display.
*
* @return the UI session instance for the given display, never <code>null</code>
*/
public static UISession getUISession( Display display ) {
ParamCheck.notNull( display, "display" );
return display.getAdapter( IDisplayAdapter.class ).getUISession();
}
/**
* Returns the <code>ApplicationContext</code> instance that represents the web context's
* global data storage area.
*
* @return instance of {@link ApplicationContext}
*/
public static ApplicationContext getApplicationContext() {
checkContext();
return ContextProvider.getApplicationContext();
}
/**
* Returns the <code>HttpServletRequest</code> that is currently processed.
* <p>
* <strong>Note:</strong> This method is <strong>not recommended</strong>. Typical application
* code should not need to call this method. Processing requests from the client is up to the
* framework. In rare cases, an application may be wish to access request details such as certain
* HTTP headers.
* </p>
*
* @return the currently processed request
*/
public static HttpServletRequest getRequest() {
checkContext();
return ContextProvider.getRequest();
}
/**
* Returns the <code>HttpServletResponse</code> that will be sent to the client after processing
* the current request.
* <p>
* <strong>Note:</strong> This method is <strong>not recommended</strong>. Typical application
* code should not need to call this method. The response should only be written and modified by
* the framework. In rare cases, an application may wish to access the response, e.g. to add a
* Cookie.
* </p>
*
* @return the response object that will be sent to the client
*/
public static HttpServletResponse getResponse() {
checkContext();
return ContextProvider.getResponse();
}
/**
* Returns the preferred <code>Locale</code> for the current UI session.
* This method is a shortcut for <code>RWT.getUISession().getLocale()</code>.
*
* @return the preferred <code>Locale</code> for the current UI session.
*
* @see UISession#getLocale()
*/
public static Locale getLocale() {
return getUISession().getLocale();
}
/**
* Sets the preferred <code>Locale</code> for the current UI session. This method is a shortcut
* for <code>RWT.getUISession().setLocale( locale )</code>.
*
* @param locale the locale to set, or <code>null</code> to reset
* @see UISession#setLocale(Locale)
*/
public static void setLocale( Locale locale ) {
getUISession().setLocale( locale );
}
/**
* Executes the run method of the given <code>runnable</code> on the
* request thread. This method may only be called from the UI thread.
* <p>
* <strong>NOTE:</strong> This API is provisional and may change without
* further notice.
* </p>
* @param runnable the code to be executed on the request thread
* @throws IllegalStateException when called from a non-UI thread
*/
public static void requestThreadExec( Runnable runnable ) {
ParamCheck.notNull( runnable, "runnable" );
checkContext();
checkPhase();
Display display = LifeCycleUtil.getSessionDisplay();
if( display == null || display.isDisposed() ) {
SWT.error( SWT.ERROR_DEVICE_DISPOSED );
}
LifeCycle lifeCycle = ContextProvider.getApplicationContext().getLifeCycleFactory().getLifeCycle();
lifeCycle.requestThreadExec( runnable );
}
/**
* Returns a representation of the client that is connected with the server in the current UI
* session. This is a shortcut for <code>RWT.getUISession().getClient()</code>.
*
* @return The client for the current UI session
* @throws IllegalStateException when called outside of the request context
*/
public static Client getClient() {
return getUISession().getClient();
}
private static void checkContext() {
if( !ContextProvider.hasContext() ) {
throw new IllegalStateException( "Invalid thread access" );
}
}
private static void checkPhase() {
if( CurrentPhase.get() == null ) {
throw new IllegalStateException( "Invalid thread access" );
}
}
private RWT() {
// prevent instantiation
}
}