/*
* Copyright (C) 2004 The Concord Consortium, Inc.,
* 10 Concord Crossing, Concord, MA 01742
*
* Web Site: http://www.concord.org
* Email: info@concord.org
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* END LICENSE */
/*
* Created on Jan 11, 2005
*
* TODO To change the template for this generated file go to
* Window - Preferences - Java - Code Style - Code Templates
*/
package org.concord.otrunk.view;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
import org.concord.framework.otrunk.OTID;
import org.concord.framework.otrunk.OTObject;
import org.concord.framework.otrunk.OTObjectList;
import org.concord.framework.otrunk.OTObjectMap;
import org.concord.framework.otrunk.OTObjectService;
import org.concord.framework.otrunk.view.OTRequestedViewEntryAware;
import org.concord.framework.otrunk.view.OTView;
import org.concord.framework.otrunk.view.OTViewContext;
import org.concord.framework.otrunk.view.OTViewContextAware;
import org.concord.framework.otrunk.view.OTViewConversionService;
import org.concord.framework.otrunk.view.OTViewEntry;
import org.concord.framework.otrunk.view.OTViewEntryAware;
import org.concord.framework.otrunk.view.OTViewFactory;
import org.concord.otrunk.OTrunkUtil;
/**
* @author scytacki
*
* TODO To change the template for this generated type comment go to
* Window - Preferences - Java - Code Style - Code Templates
*/
public class OTViewFactoryImpl implements OTViewFactory, OTLifecycleNotifying
{
private static final Logger logger =
Logger.getLogger(OTViewFactoryImpl.class.getCanonicalName());
OTViewFactoryImpl parent;
ArrayList<InternalViewEntry> viewMap = new ArrayList<InternalViewEntry>();
ArrayList<OTViewBundle> viewBundles = new ArrayList<OTViewBundle>();
OTViewContextImpl viewContext;
private String mode;
ArrayList<OTLifecycleListener> lifecycleListeners = new ArrayList<OTLifecycleListener>();
public OTViewFactoryImpl(OTViewBundle viewBundle)
{
this.viewBundles.add(viewBundle);
// read in all the viewEntries and create a vector
// of class entries.
OTObjectList viewEntries = viewBundle.getViewEntries();
for(int i=0; i<viewEntries.size(); i++) {
OTViewEntry entry = (OTViewEntry)viewEntries.get(i);
addViewEntry(entry);
}
initServices();
}
protected OTViewFactoryImpl(OTViewFactoryImpl parent)
{
this.parent = parent;
initServices();
}
protected void initServices()
{
OTViewContext viewContextParent = null;
if(parent != null){
viewContextParent = parent.getViewContext();
}
viewContext = new OTViewContextImpl(this, viewContextParent);
// we might have a view conversion service registered already, so don't override it.
if(viewContext.getViewService(OTViewConversionService.class) == null){
viewContext.addViewService(OTViewConversionService.class,
new OTViewConversionServiceImpl());
}
}
/**
* This should be called after the bundles have been added and any view services
* have been added to the viewContext
*/
public void contextSetupComplete()
{
for(InternalViewEntry viewEntry: viewMap){
try {
Method otInit = viewEntry.viewClass.getMethod("otInit", OTViewContext.class);
otInit.invoke(null, getViewContext());
} catch (NoSuchMethodException e) {
// this class doesn't have an otInit method
} catch (NoClassDefFoundError e){
// this class can't be loaded because of a missing dependency
logger.warning("Missing dependency : " + e.getMessage() +
" for view class: " + viewEntry.viewClass.getCanonicalName());
} catch (Throwable t) {
t.printStackTrace();
}
}
notifyLifecycleListeners(OTLifecycleEvent.INITIALIZATION_COMPLETE);
}
class InternalViewEntry {
Class<? extends OTObject> objectClass;
Class<? extends OTView> viewClass;
OTID otEntryID;
/**
* The object service is used so the otview entry can be overridden by
* the layering system.
*
* @param objService
* @return
*/
OTViewEntry getOTViewEntry(OTObjectService objService)
{
OTViewEntry otViewEntry;
try {
otViewEntry = (OTViewEntry) objService.getOTObject(otEntryID);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
}
return otViewEntry;
}
}
public OTViewFactory createChildViewFactory()
{
return new OTViewFactoryImpl(this);
}
/**
* @see org.concord.otrunk.view.OTViewFactory#getView(org.concord.framework.otrunk.OTObject, java.lang.Class)
*/
public <T extends OTView> T getView(OTObject otObject, Class<T> viewInterface)
{
return getView(otObject, viewInterface, null);
}
/**
* @see org.concord.framework.otrunk.view.OTViewFactory#getView(org.concord.framework.otrunk.OTObject, java.lang.Class, java.lang.String)
*/
@SuppressWarnings("unchecked")
public <T extends OTView> T getView(OTObject otObject, Class<T> viewInterface, String modeStr)
{
InternalViewEntry entry = getViewInternal(otObject, viewInterface);
if(entry == null) {
// we did not find the view using its specific viewInterface, but perhaps there is a view
// with a different interface and the mode provide a view with the correct interface
// for efficiency we should check if a view mode will be used.
entry = getViewInternal(otObject, (Class<?>)null);
if(entry == null) {
return null;
}
}
OTObjectService objService = otObject.getOTObjectService();
OTViewEntry viewEntry = entry.getOTViewEntry(objService);
OTView view = getView(otObject, viewEntry, modeStr);
// We need to do a final check here because we might have ignored the passed in viewInterface
// above.
if(!viewInterface.isInstance(view)){
return null;
}
return (T)view;
}
protected void initView(OTView view, OTViewEntry viewEntry)
{
if(view instanceof OTViewContextAware) {
((OTViewContextAware)view).setViewContext(viewContext);
}
if(view instanceof OTViewEntryAware) {
((OTViewEntryAware)view).setViewEntry(viewEntry);
}
}
public OTView getView(OTObject otObject, OTViewEntry viewEntry)
{
return getView(otObject, viewEntry, null);
}
private OTView getViewInternal(OTObject otObject, OTViewEntry viewEntry)
{
// because we have the view entry we don't need to actually
// look up this view.
String viewClassStr = viewEntry.getViewClass();
String objClassStr = viewEntry.getObjectClass();
ClassLoader loader = getClass().getClassLoader();
try {
Class<?> objectClass = loader.loadClass(objClassStr);
if(!objectClass.isInstance(otObject)){
throw new RuntimeException("viewEntry: " + viewEntry +
" cannot handle otObject: " + otObject);
}
if(viewClassStr == null){
throw new RuntimeException("viewEntry " + viewEntry +
" has a null viewClass");
}
OTView view = null;
Class<?> viewClass = loader.loadClass(viewClassStr);
view = (OTView)viewClass.newInstance();
initView(view, viewEntry);
return view;
} catch (ClassNotFoundException e) {
logger.warning(
"Can't find view: " + viewClassStr + " for object: " + objClassStr + "\n" +
" error: " + e.toString());
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
/**
* null is allowed for the viewInterface.
*
* @param otObject
* @param viewInterface
* @return
*/
private InternalViewEntry getViewInternal(OTObject otObject, Class<?> viewInterface)
{
for(InternalViewEntry entry: viewMap){
if(entry.objectClass.isInstance(otObject) &&
(viewInterface == null || viewInterface.isAssignableFrom(entry.viewClass))) {
return entry;
}
}
// can't find the view in our own list
// check parent
if(parent != null) {
return parent.getViewInternal(otObject, viewInterface);
}
return null;
}
public void addViewEntry(OTViewEntry entry)
{
InternalViewEntry internalEntry = createInternalViewEntry(entry);
if(internalEntry == null){
return;
}
viewMap.add(internalEntry);
}
@SuppressWarnings("unchecked")
protected InternalViewEntry createInternalViewEntry(OTViewEntry entry)
{
String objClassStr = entry.getObjectClass();
String viewClassStr = entry.getViewClass();
ClassLoader loader = getClass().getClassLoader();
try {
InternalViewEntry internalEntry = new InternalViewEntry();
internalEntry.objectClass = (Class<? extends OTObject>) loader.loadClass(objClassStr);
if(viewClassStr != null){
internalEntry.viewClass = (Class<? extends OTView>) loader.loadClass(viewClassStr);
}
internalEntry.otEntryID = entry.getGlobalId();
return internalEntry;
} catch (ClassNotFoundException e) {
logger.warning(
"Can't find view: " + viewClassStr + " for object: " + objClassStr + "\n" +
" error: " + e.toString());
}
return null;
}
/* (non-Javadoc)
* @see org.concord.otrunk.view.OTViewFactory#addViewEntry(java.lang.Class, java.lang.Class)
*/
public void addViewEntry(OTViewEntry entry, boolean addToTop)
{
InternalViewEntry internalEntry = createInternalViewEntry(entry);
if(internalEntry == null){
return;
}
if (addToTop){
viewMap.add(0, internalEntry);
} else {
viewMap.add(internalEntry);
}
}
/**
* This will return the viewEntry setup by this mode. If no viewEntry is
* found for this mode, then it will return null.
*
* If modeStr == null then getDefaultViewMode will be used to look up the
* view mode.
* If modeStr equals NO_VIEW_MODE then this method will return null;
*
* @param viewEntry
* @param modeStr
* @return
*/
protected OTViewEntry getModeViewEntry(OTViewEntry viewEntry, String modeStr)
{
if(modeStr == null){
modeStr = getDefaultViewMode();
}
if(NO_VIEW_MODE.equals(modeStr)){
return null;
}
OTViewMode firstMode = null;
OTViewEntry modeViewEntry = null;
List<OTViewBundle> allViewBundles = getViewBundles();
for(int j=0; j<allViewBundles.size() && modeViewEntry == null; j++){
OTViewBundle viewBundle = allViewBundles.get(j);
OTObjectList modes = viewBundle.getModes();
for(int i=0; i<modes.size(); i++){
OTViewMode curMode = (OTViewMode)modes.get(i);
if(curMode.getName().equals(modeStr)){
if(firstMode == null) {
firstMode = curMode;
}
OTObjectMap map = curMode.getMap();
modeViewEntry =
(OTViewEntry)OTrunkUtil.getObjectFromMapWithIdKeys(map, viewEntry);
break;
}
}
}
if(firstMode == null){
logger.warning("Cannot find view mode: \"" + modeStr + "\"");
return null;
}
if(modeViewEntry == null){
modeViewEntry = firstMode.getDefault();
}
return modeViewEntry;
}
/* (non-Javadoc)
* @see org.concord.framework.otrunk.view.OTViewFactory#getView(org.concord.framework.otrunk.OTObject, org.concord.framework.otrunk.view.OTViewEntry, java.lang.String)
*/
public OTView getView(OTObject otObject, OTViewEntry viewEntry, String modeStr)
{
OTViewEntry activeViewEntry = viewEntry;
OTViewEntry modeViewEntry = getModeViewEntry(viewEntry, modeStr);
if(modeViewEntry != null){
activeViewEntry = modeViewEntry;
}
OTView view = getViewInternal(otObject, activeViewEntry);
// if the modeViewEntry is not null then
// pass the viewEntry that was requested to the newly created view
// this is useful for mode views that want to display other modes of
// the original view entry.
// this entry might have been specified by the user, or it could have
// been determined by looking up an interface and object type.
if(modeViewEntry != null &&
view instanceof OTRequestedViewEntryAware) {
((OTRequestedViewEntryAware)view).setRequestedViewEntry(viewEntry);
}
return view;
}
/**
* Return a combined list of view bundles.
* @return
*/
public List<OTViewBundle> getViewBundles()
{
ArrayList<OTViewBundle> combinedBundles = new ArrayList<OTViewBundle>();
combinedBundles.addAll(viewBundles);
if (!(parent == null)){
combinedBundles.addAll(parent.getViewBundles());
}
return combinedBundles;
}
/* (non-Javadoc)
* @see org.concord.framework.otrunk.view.OTViewFactory#getViewServiceProvider()
*/
public OTViewContext getViewContext()
{
return viewContext;
}
/**
* Return the view mode set for this view factory. If this is
* null then getDefaultViewMode will try to return the view mode
* off the parent.
*
* @return
*/
public String getDefaultViewModeLocal()
{
return mode;
}
/**
* This returns the view mode used if null is passed in for the view mode
* to the getView methods.
*
* @see org.concord.framework.otrunk.view.OTViewFactory#getDefaultViewMode()
*/
public String getDefaultViewMode()
{
if (mode == null && parent != null){
return parent.getDefaultViewMode();
}
if(mode == null){
return NO_VIEW_MODE;
}
return mode;
}
/**
* This sets the default view mode for this view factory. If it is null
* then the viewMode of the parent is used. If this is set to null
* and there is no parent, then NO_VIEW_MODE is used.
*
* @see org.concord.framework.otrunk.view.OTViewFactory#setDefaultViewMode(java.lang.String)
*/
public void setDefaultViewMode(String mode)
{
this.mode = mode;
}
public String [] getModeNames()
{
ArrayList<String> names = new ArrayList<String>();
for(OTViewBundle bundle: getViewBundles()){
for (OTObject curMode : bundle.getModes()) {
if(!names.contains(curMode.getName())){
names.add(curMode.getName());
}
}
}
return names.toArray(new String[names.size()]);
}
/**
* Adds all the viewEntries from the bundle
* Overrides the default view mode.
* Adds the bundle to the beginning of the list of bundles which is used to find view mode view entries.
*
* @param viewBundle
*/
public void addViewBundle(OTViewBundle viewBundle)
{
// Add view entries
ArrayList<InternalViewEntry> tempViewMap = new ArrayList<InternalViewEntry>();
for(OTObject entry: viewBundle.getViewEntries()){
InternalViewEntry internalViewEntry = createInternalViewEntry((OTViewEntry) entry);
if(internalViewEntry == null){
continue;
}
tempViewMap.add(internalViewEntry);
}
viewMap.addAll(0, tempViewMap);
// Override currentMode
if (viewBundle.getCurrentMode() != null){
setDefaultViewMode(viewBundle.getCurrentMode());
}
viewBundles.add(0, viewBundle);
}
private void notifyLifecycleListeners(int type) {
OTLifecycleEvent event = new OTLifecycleEvent(type, this);
for (OTLifecycleListener listener : lifecycleListeners) {
listener.lifecycleEventOccurred(event);
}
}
public void addLifecycleListener(OTLifecycleListener listener)
{
if (! this.lifecycleListeners.contains(listener)) {
this.lifecycleListeners.add(listener);
}
}
public void removeLifecycleListener(OTLifecycleListener listener)
{
this.lifecycleListeners.remove(listener);
}
}