/*
* Copyright 2001-2008 Geert Bevin <gbevin[remove] at uwyn dot com>
* Licensed under the Apache License, Version 2.0 (the "License")
* $Id: CrudSiteProcessor.java 3953 2008-05-08 01:04:08Z gbevin $
*/
package com.uwyn.rife.crud;
import com.uwyn.rife.crud.elements.admin.*;
import com.uwyn.rife.config.Config;
import com.uwyn.rife.config.RifeConfig;
import com.uwyn.rife.database.Datasource;
import com.uwyn.rife.database.Datasources;
import com.uwyn.rife.database.querymanagers.generic.GenericQueryManager;
import com.uwyn.rife.database.querymanagers.generic.GenericQueryManagerFactory;
import com.uwyn.rife.database.querymanagers.generic.GenericQueryManagerRelationalUtils;
import com.uwyn.rife.database.querymanagers.generic.ManyToOneDeclaration;
import com.uwyn.rife.engine.ElementInfoBuilder;
import com.uwyn.rife.engine.SiteBuilder;
import com.uwyn.rife.engine.SiteProcessor;
import com.uwyn.rife.engine.SiteProcessorFactory;
import com.uwyn.rife.engine.UrlResource;
import com.uwyn.rife.engine.exceptions.EngineException;
import com.uwyn.rife.ioc.PropertyValueObject;
import com.uwyn.rife.resources.ResourceFinder;
import com.uwyn.rife.resources.exceptions.ResourceFinderErrorException;
import com.uwyn.rife.site.Constrained;
import com.uwyn.rife.site.ConstrainedBean;
import com.uwyn.rife.site.ConstrainedProperty;
import com.uwyn.rife.site.ConstrainedUtils;
import com.uwyn.rife.site.PagedNavigation;
import com.uwyn.rife.tools.BeanUtils;
import com.uwyn.rife.tools.ClassUtils;
import com.uwyn.rife.tools.exceptions.BeanUtilsException;
import java.net.URL;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import static com.uwyn.rife.crud.CrudPropertyNames.*;
public class CrudSiteProcessor implements SiteProcessor
{
public final static String CRUD_PREFIX = "crud-";
public final static String IDENTIFIER_MANYTOONE_PROPERTYNAME;
public final static String IDENTIFIER_ASSOCIATED_CLASSNAME;
public final static String IDENTIFIER_IMPLEMENTATION;
public final static String IDENTIFIER_CLASS;
public final static String IDENTIFIER_HIGHLIGHT;
public final static String IDENTIFIER_ELEMENTID_HOME;
public final static String IDENTIFIER_ELEMENTID_MENU;
public final static String IDENTIFIER_ELEMENTID_BROWSE;
public final static String IDENTIFIER_ELEMENTID_ADD;
public final static String IDENTIFIER_ELEMENTID_SERVECONTENT;
public final static String IDENTIFIER_ELEMENTID_EDIT;
public final static String IDENTIFIER_ELEMENTID_DELETE;
public final static String IDENTIFIER_ELEMENTID_MOVE;
public final static String IDENTIFIER_SUBSITEID_MODIFICATION;
public final static String IDENTIFIER_SUBSITEELEMENTID_EDIT;
public final static String IDENTIFIER_SUBSITEELEMENTID_DELETE;
public final static String IDENTIFIER_SUBSITEELEMENTID_MOVE;
public final static String SUFFIX_EXIT_HOME = "-home";
public final static String SUFFIX_EXIT_BROWSE = "-browse";
public final static String SUFFIX_EXIT_ADD = "-add";
public final static String SUFFIX_EXIT_EDIT = "-edit";
public final static String SUFFIX_EXIT_DELETE = "-delete";
public final static String SUFFIX_EXIT_SERVECONTENT = "-servecontent";
static
{
IDENTIFIER_MANYTOONE_PROPERTYNAME = CRUD_PREFIX+"manytoone_propertyname";
IDENTIFIER_ASSOCIATED_CLASSNAME = CRUD_PREFIX+"associated_classname";
IDENTIFIER_IMPLEMENTATION = CRUD_PREFIX+"implementation";
IDENTIFIER_CLASS = CRUD_PREFIX+"class";
IDENTIFIER_HIGHLIGHT = CRUD_PREFIX+"highlight";
IDENTIFIER_ELEMENTID_HOME = "HOME";
IDENTIFIER_ELEMENTID_MENU = "MENU";
IDENTIFIER_ELEMENTID_BROWSE = "BROWSE";
IDENTIFIER_ELEMENTID_ADD = "ADD";
IDENTIFIER_ELEMENTID_SERVECONTENT = "SERVECONTENT";
IDENTIFIER_ELEMENTID_EDIT = "EDIT";
IDENTIFIER_ELEMENTID_DELETE = "DELETE";
IDENTIFIER_ELEMENTID_MOVE = "MOVE";
IDENTIFIER_SUBSITEID_MODIFICATION = "MODIFICATION";
IDENTIFIER_SUBSITEELEMENTID_EDIT = IDENTIFIER_SUBSITEID_MODIFICATION+"."+IDENTIFIER_ELEMENTID_EDIT;
IDENTIFIER_SUBSITEELEMENTID_DELETE = IDENTIFIER_SUBSITEID_MODIFICATION+"."+IDENTIFIER_ELEMENTID_DELETE;
IDENTIFIER_SUBSITEELEMENTID_MOVE = IDENTIFIER_SUBSITEID_MODIFICATION+"."+IDENTIFIER_ELEMENTID_MOVE;
}
public void processSite(SiteBuilder builder, String implementation, ResourceFinder resourceFinder)
{
try
{
Class klass = BeanClassFactory.INSTANCE.getClassInstance(implementation);
if (RifeConfig.Engine.getSiteAutoReload())
{
URL resource = BeanClassFactory.INSTANCE.getClassResource(implementation);
builder.addResourceModificationTime(new UrlResource(resource, implementation), resourceFinder.getModificationTime(resource));
}
buildAdminSubsite(builder, implementation, klass, null, null);
}
catch (ClassNotFoundException e)
{
throw new EngineException(e);
}
catch (ResourceFinderErrorException e)
{
throw new EngineException(e);
}
}
public void buildAdminSubsite(SiteBuilder builder, String implementation, Class klass, String manytooneProperty, String associatedClassname)
throws EngineException
{
String classname = klass.getName();
String short_classname = ClassUtils.shortenClassName(klass);
String submission_name = short_classname+"data";
String identity_property = ConstrainedUtils.getIdentityProperty(klass);
String identity_property_prefixed = short_classname+identity_property;
String ordinal_property_name = null;
HashMap<Class, String> associations_columns = null;
// analyze the class to check for special features such as ordinal properties
Constrained constrained = ConstrainedUtils.getConstrainedInstance(klass);
Collection<ConstrainedProperty> properties = null;
if (constrained != null)
{
properties = constrained.getConstrainedProperties();
// detect ordinal properties
for (ConstrainedProperty property : properties)
{
if (null == ordinal_property_name &&
property.isOrdinal())
{
ordinal_property_name = property.getPropertyName();
}
}
// detect associations
if (constrained.getConstrainedBean() != null &&
constrained.getConstrainedBean().hasAssociations())
{
Datasource datasource = (Datasource)builder.getProperty("datasource", Datasources.getRepInstance().getDatasource(Config.getRepInstance().getString("CRUD_DATASOURCE", Config.getRepInstance().getString("DATASOURCE"))));
GenericQueryManager gqm = GenericQueryManagerFactory.getInstance(datasource, klass);
String gqm_table_name = gqm.getTable();
for (Class association : constrained.getConstrainedBean().getAssociations())
{
try
{
Map<String, Class> property_types = BeanUtils.getPropertyTypes(association, null, null, null);
Constrained<ConstrainedBean, ConstrainedProperty> constrained_association = ConstrainedUtils.getConstrainedInstance(association);
if (constrained_association != null)
{
for (ConstrainedProperty property : constrained_association.getConstrainedProperties())
{
ManyToOneDeclaration declaration = GenericQueryManagerRelationalUtils.createManyToOneDeclaration(gqm, property, property_types.get(property.getName()));
if (declaration != null &&
gqm_table_name.equals(declaration.getAssociationTable()) &&
identity_property.equals(declaration.getAssociationColumn()))
{
if (null == associations_columns)
{
associations_columns = new LinkedHashMap<Class, String>();
}
associations_columns.put(association, property.getPropertyName());
break;
}
}
}
}
catch (BeanUtilsException e)
{
throw new EngineException(e);
}
}
}
}
// generate the class-specific crud prefix
String crud_prefix = CRUD_PREFIX+classname;
// generate the class-specific global exit names
String exit_home = crud_prefix+SUFFIX_EXIT_HOME;
String exit_browse = crud_prefix+SUFFIX_EXIT_BROWSE;
String exit_add = crud_prefix+SUFFIX_EXIT_ADD;
String exit_edit = crud_prefix+SUFFIX_EXIT_EDIT;
String exit_delete = crud_prefix+SUFFIX_EXIT_DELETE;
String exit_servecontent = crud_prefix+SUFFIX_EXIT_SERVECONTENT;
builder
.setArrival(IDENTIFIER_ELEMENTID_BROWSE);
if (null == associatedClassname)
{
builder
.addGlobalExit(crud_prefix, "");
}
builder
.addGlobalExit(exit_home, IDENTIFIER_ELEMENTID_HOME)
.addGlobalExit(exit_browse, IDENTIFIER_ELEMENTID_BROWSE)
.addGlobalExit(exit_add, IDENTIFIER_ELEMENTID_ADD)
.addGlobalExit(exit_edit, IDENTIFIER_SUBSITEELEMENTID_EDIT)
.addGlobalExit(exit_delete, IDENTIFIER_SUBSITEELEMENTID_DELETE)
.addGlobalExit(exit_servecontent, IDENTIFIER_ELEMENTID_SERVECONTENT)
.addGlobalVar(PagedNavigation.DEFAULT_OUTPUT)
.addProperty(IDENTIFIER_IMPLEMENTATION, new PropertyValueObject(implementation))
.addProperty(IDENTIFIER_CLASS, new PropertyValueObject(klass))
.enterElement(IDENTIFIER_ELEMENTID_MENU)
.setImplementation(builder.getPropertyString(IMPLEMENTATION_MENU, Menu.class.getName()))
.leaveElement()
.enterElement("rife/cmf/serve_content.xml")
.setId(IDENTIFIER_ELEMENTID_SERVECONTENT)
.setUrl("/content/*")
.leaveElement();
ElementInfoBuilder home_element= builder
.enterElement(IDENTIFIER_ELEMENTID_HOME)
.setImplementation(builder.getPropertyString(IMPLEMENTATION_HOME, Home.class.getName()))
.setUrl("/home")
.addProperty(IDENTIFIER_HIGHLIGHT, new PropertyValueObject(crud_prefix));
ElementInfoBuilder add_element = builder
.enterElement(IDENTIFIER_ELEMENTID_ADD)
.setImplementation(builder.getPropertyString(IMPLEMENTATION_ADD, Add.class.getName()))
.setUrl("/add")
.addProperty(IDENTIFIER_HIGHLIGHT, new PropertyValueObject(crud_prefix))
.enterSubmission(submission_name)
.addBean(klass)
.leaveSubmission();
ElementInfoBuilder browse_element = builder
.enterElement(IDENTIFIER_ELEMENTID_BROWSE)
.setImplementation(builder.getPropertyString(IMPLEMENTATION_BROWSE, Browse.class.getName()))
.setUrl("/browse")
.addProperty(IDENTIFIER_HIGHLIGHT, new PropertyValueObject(crud_prefix))
.addExit(PagedNavigation.DEFAULT_EXIT)
.addFlowLink(PagedNavigation.DEFAULT_EXIT, IDENTIFIER_ELEMENTID_BROWSE)
.addOutput(identity_property_prefixed)
.addDataLink(identity_property_prefixed, IDENTIFIER_SUBSITEELEMENTID_EDIT, identity_property_prefixed)
.addDataLink(identity_property_prefixed, IDENTIFIER_SUBSITEELEMENTID_DELETE, identity_property_prefixed);
SiteBuilder modification_subsite_builder = builder
.enterSubsiteDeclaration(SiteProcessorFactory.MANUAL_IDENTIFIER+":"+IDENTIFIER_SUBSITEID_MODIFICATION)
.setId(IDENTIFIER_SUBSITEID_MODIFICATION)
.enterSubsite()
.addGlobalVar(identity_property_prefixed);
ElementInfoBuilder edit_element = modification_subsite_builder
.enterElement(IDENTIFIER_ELEMENTID_EDIT)
.setImplementation(builder.getPropertyString(IMPLEMENTATION_EDIT, Edit.class.getName()))
.setUrl("/edit")
.addProperty(IDENTIFIER_HIGHLIGHT, new PropertyValueObject(exit_browse))
.enterSubmission(submission_name)
.addBean(klass)
.leaveSubmission();
ElementInfoBuilder delete_element = modification_subsite_builder
.enterElement(IDENTIFIER_ELEMENTID_DELETE)
.setImplementation(builder.getPropertyString(IMPLEMENTATION_DELETE, Delete.class.getName()))
.setUrl("/delete")
.addProperty(IDENTIFIER_HIGHLIGHT, new PropertyValueObject(exit_browse))
.enterSubmission("confirm")
.leaveSubmission();
// store the manytoone property name
if (manytooneProperty != null)
{
// add the required element properties
home_element
.addProperty(IDENTIFIER_MANYTOONE_PROPERTYNAME, new PropertyValueObject(manytooneProperty))
.addProperty(IDENTIFIER_ASSOCIATED_CLASSNAME, new PropertyValueObject(associatedClassname));
browse_element
.addProperty(IDENTIFIER_MANYTOONE_PROPERTYNAME, new PropertyValueObject(manytooneProperty))
.addProperty(IDENTIFIER_ASSOCIATED_CLASSNAME, new PropertyValueObject(associatedClassname));
add_element
.addProperty(IDENTIFIER_MANYTOONE_PROPERTYNAME, new PropertyValueObject(manytooneProperty))
.addProperty(IDENTIFIER_ASSOCIATED_CLASSNAME, new PropertyValueObject(associatedClassname));
edit_element
.addProperty(IDENTIFIER_MANYTOONE_PROPERTYNAME, new PropertyValueObject(manytooneProperty))
.addProperty(IDENTIFIER_ASSOCIATED_CLASSNAME, new PropertyValueObject(associatedClassname));
delete_element
.addProperty(IDENTIFIER_MANYTOONE_PROPERTYNAME, new PropertyValueObject(manytooneProperty))
.addProperty(IDENTIFIER_ASSOCIATED_CLASSNAME, new PropertyValueObject(associatedClassname));
}
// build association subsites
if (associations_columns != null)
{
for (Class assocation : associations_columns.keySet())
{
String name = ClassUtils.shortenClassName(assocation);
modification_subsite_builder.addGlobalExit(CRUD_PREFIX+assocation.getName(), name);
}
for (Class assocation : associations_columns.keySet())
{
// setup the subsite declaration
String name = ClassUtils.shortenClassName(assocation);
String declaration = SiteProcessorFactory.MANUAL_IDENTIFIER+":"+ClassUtils.shortenClassName(assocation);
SiteBuilder subsite = modification_subsite_builder
.enterSubsiteDeclaration(declaration)
.setId(name)
.setUrlPrefix(name)
.enterSubsite();
// build the actual subsite recursively
buildAdminSubsite(subsite, assocation.getName(), assocation, associations_columns.get(assocation), klass.getName());
// setup links from the browse element of the associated class
builder
.addProperty(crud_prefix+"-associations_columns", new PropertyValueObject(associations_columns));
String association_crud_prefix = CRUD_PREFIX+assocation.getName();
String association_exit_browse = association_crud_prefix+"-browse";
String association_exit_add = association_crud_prefix+"-add";
String association_target_browse = IDENTIFIER_SUBSITEID_MODIFICATION+"."+name+"."+IDENTIFIER_ELEMENTID_BROWSE;
String association_target_add = IDENTIFIER_SUBSITEID_MODIFICATION+"."+name+"."+IDENTIFIER_ELEMENTID_ADD;
browse_element
.addExit(association_exit_browse)
.addExit(association_exit_add)
.addFlowLink(association_exit_browse, association_target_browse)
.addFlowLink(association_exit_add, association_target_add)
.addDataLink(identity_property_prefixed, association_target_browse, identity_property_prefixed)
.addDataLink(identity_property_prefixed, association_target_add, identity_property_prefixed);
}
}
// add the ordinal property name if it exists and create the appropriate additional elements
if (ordinal_property_name != null)
{
builder
.addProperty(crud_prefix+"-ordinal_property_name", new PropertyValueObject(ordinal_property_name));
browse_element
.addOutput("direction")
.addExit("up")
.addExit("down")
.addFlowLink("up", IDENTIFIER_SUBSITEELEMENTID_MOVE)
.addFlowLink("down", IDENTIFIER_SUBSITEELEMENTID_MOVE)
.addDataLink(identity_property_prefixed, IDENTIFIER_SUBSITEELEMENTID_MOVE, identity_property_prefixed)
.addDataLink("direction", IDENTIFIER_SUBSITEELEMENTID_MOVE, "direction");
modification_subsite_builder
.enterElement(IDENTIFIER_ELEMENTID_MOVE)
.setImplementation(builder.getPropertyString(IMPLEMENTATION_MOVE, Move.class.getName()))
.setUrl("/move")
.addInput("direction")
.addExit("moved")
.addFlowLink("moved", "^"+IDENTIFIER_ELEMENTID_BROWSE)
.leaveElement();
}
}
}