/*******************************************************************************
* Copyright (c) 2002-2006 Innoopract Informationssysteme GmbH.
* 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
******************************************************************************/
package com.w4t;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
/** <p>Implements the basic functionality which is common to every
* WebObject based component. </p>
*/
public abstract class WebObject implements Cloneable {
/** whether the event listener fiels in this WebObject should be cloned. */
private boolean cloneListeners = true;
/** Constructor */
protected WebObject() {
}
/** uses the Class.getDeclaredFields() in order to get a list of
* declared fields in this WebObject, including inherited fields
* from all classes above in the hierarchy. */
protected Field[] getDeclaredFields() {
Class thisClass;
Class superClass;
Field[] fields;
try {
thisClass = this.getClass();
superClass = thisClass.getSuperclass();
fields = thisClass.getDeclaredFields();
Field[] superFields = null;
Field[] tempFields = null;
boolean woReached = false;
while ( !woReached ) {
// get superclass
if( !superClass.isAssignableFrom(
Class.forName( "java.lang.Object" ) ) ) {
// get declared fields from the next class above in the hierarchy
superFields = superClass.getDeclaredFields();
superClass = superClass.getSuperclass();
// merge the fields arrays
tempFields = new Field[ fields.length + superFields.length ];
System.arraycopy( fields, 0, tempFields, 0, fields.length );
System.arraycopy( superFields, 0,
tempFields, fields.length, superFields.length );
fields = tempFields;
} else {
woReached = true;
}
} // while
} catch ( final ClassNotFoundException shouldNotHappen ) {
throw new RuntimeException( shouldNotHappen );
}
return fields;
}
/** returns a clone of this WebObject.<br>
* For all elementary WebComponents, cloning will result in a deep copy,
* shallow copying all fields, cloning property objects (i.e. all
* subclasses of WebComponentProperties, like Style, WindowProperties
* etc.), and in case the WebObject to clone is a WebContainer, also
* cloning all WebComponents, if they were added to it using
* WebContainer.add(), and the WebLayout, if it was set using
* WebContainer.setWebLayout(). Fields containing external references to
* Objects which are not added with one of those methods are set to null
* in the cloned object.
*/
public Object clone() throws CloneNotSupportedException {
Object clone = super.clone();
try {
Field[] fields = this.getDeclaredFields(); // we use our own
// getDeclaredFields()
Class fieldType = null;
int length = fields.length;
for ( int i = 0; i < length; i++ ) {
if( !isStatic( fields[ i ] ) && !isFinal( fields[ i ] ) ) {
fieldType = fields[ i ].getType();
if( fieldType.isPrimitive() ) {
// it should have been copied at super.clone()
} else if ( fieldType.isAssignableFrom(
Class.forName( "java.lang.String" ) ) ) {
// it should have been copied at super.clone()
} else {
try {
if( !isListener( fields[ i ] )
|| !isCloneListeners() ) {
fields[ i ].setAccessible( true );
fields[ i ].set( ( WebObject )clone, null );
}
} catch ( IllegalAccessException iace ) {
System.err.println( "Exception occured in 'WebObject.clone()' "
+ "cloning field '"
+ fields[ i ].getName() + "'\n"
+ iace.toString() );
} catch ( IllegalArgumentException iarge ) {
System.err.println( "Exception occured in 'WebObject.clone()'. "
+ "cloning field '"
+ fields[ i ].getName() + "'\n"
+ iarge.toString() );
}
}
}
} // for fields
} catch ( Exception ex ) {
System.err.println( "Exception occured in 'WebObject.clone()'. "
+ ex.toString() );
}
return clone;
}
// helping methods
//////////////////
private boolean isFinal( final Field field ) {
int mod = field.getModifiers();
return Modifier.isFinal( mod );
}
private boolean isStatic( final Field field ) {
int mod = field.getModifiers();
return Modifier.isStatic( mod );
}
private boolean isListener( final Field field ) {
return checkListenerExtension( field.getType().getName() )
&& checkEventClass( field );
}
private boolean checkListenerExtension( final String fieldName ) {
return terminatesAfterListenerEnding( fieldName );
}
private boolean checkEventClass( final Field field ) {
boolean result = false;
String eventName = createEventName( field );
try {
Class.forName( eventName );
result = true;
} catch( ClassNotFoundException canBeIgnored ) {
// because we use it only to see whether the class is accessible,
// if not, we act accordingly
}
return result;
}
private int getListenerIndex( final String name ) {
return name.lastIndexOf( "Listener" );
}
private boolean hasListenerEnding( final String name ) {
return getListenerIndex( name ) != -1;
}
private boolean terminatesAfterListenerEnding( final String name ) {
return hasListenerEnding( name )
&& name.length() == getListenerIndex( name ) + 8;
}
private String createEventName( final Field field ) {
String result = field.getType().getName();
result = result.substring( 0, getListenerIndex( result ) ) + "Event";
return result;
}
// attribute getters and setters
////////////////////////////////
/** sets whether the event listener fields in this WebObject should be
* cloned. */
public void setCloneListeners( final boolean cloneListeners ) {
this.cloneListeners = cloneListeners;
}
/** returns whether the event listener fields in this WebObject should be
* cloned. */
public boolean isCloneListeners() {
return cloneListeners;
}
}