/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package com.venky.swf.path;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.StringTokenizer;
import java.util.TreeMap;
import javax.activation.MimetypesFileTypeMap;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemFactory;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.lang3.StringEscapeUtils;
import com.venky.cache.Cache;
import com.venky.core.collections.LowerCaseStringCache;
import com.venky.core.collections.SequenceSet;
import com.venky.core.io.ByteArrayInputStream;
import com.venky.core.log.SWFLogger;
import com.venky.core.log.TimerStatistics.Timer;
import com.venky.core.string.StringUtil;
import com.venky.core.util.ObjectUtil;
import com.venky.extension.Registry;
import com.venky.swf.controller.Controller;
import com.venky.swf.controller.annotations.Depends;
import com.venky.swf.controller.reflection.ControllerReflector;
import com.venky.swf.db.Database;
import com.venky.swf.db.annotations.column.pm.PARTICIPANT;
import com.venky.swf.db.annotations.column.relationship.CONNECTED_VIA;
import com.venky.swf.db.annotations.column.ui.mimes.MimeType;
import com.venky.swf.db.model.Model;
import com.venky.swf.db.model.User;
import com.venky.swf.db.model._Identifiable;
import com.venky.swf.db.model.reflection.ModelReflector;
import com.venky.swf.db.table.BindVariable;
import com.venky.swf.db.table.Table;
import com.venky.swf.exceptions.AccessDeniedException;
import com.venky.swf.exceptions.MultiException;
import com.venky.swf.pm.DataSecurityFilter;
import com.venky.swf.routing.Config;
import com.venky.swf.sql.Conjunction;
import com.venky.swf.sql.Expression;
import com.venky.swf.sql.Operator;
import com.venky.swf.sql.Select;
import com.venky.swf.sql.parser.SQLExpressionParser;
import com.venky.swf.views.HtmlView.StatusType;
import com.venky.swf.views.RedirectorView;
import com.venky.swf.views.View;
import com.venky.swf.views._IView;
import com.venky.swf.views.controls.model.ModelAwareness;
/**
*
* @author venky
*/
public class Path implements _IPath{
static {
Config.instance().getLogger(Path.class.getName()).info("Loaded by " + Path.class.getClassLoader());
}
private List<String> pathelements = new ArrayList<String>();
private String controllerClassName = null;
private int controllerPathIndex = 0;
private int actionPathIndex = 1 ;
private int parameterPathIndex = 2;
private String target = null;
private HttpSession session = null ;
private HttpServletRequest request = null ;
private HttpServletResponse response = null ;
private Map<String,Object> formFields = null;
@SuppressWarnings("unchecked")
public Map<String, Object> getFormFields(){
if (formFields != null){
return formFields;
}
formFields = new HashMap<String,Object>();
Map<String,Object> formInput = new HashMap<String, Object>();
HttpServletRequest request = getRequest();
boolean isMultiPart = ServletFileUpload.isMultipartContent(request);
if (isMultiPart){
FileItemFactory factory = new DiskFileItemFactory(1024*1024*128, new File(System.getProperty("java.io.tmpdir")));
ServletFileUpload fu = new ServletFileUpload(factory);
try {
List<FileItem> fis = fu.parseRequest(request);
for (FileItem fi:fis){
if (fi.isFormField()){
if (!formInput.containsKey(fi.getFieldName())){
formInput.put(fi.getFieldName(), fi.getString());
}
}else {
byte[] content = StringUtil.readBytes(fi.getInputStream());
if (content == null || content.length == 0){
content = null;
}else {
formInput.put(fi.getFieldName() + "_CONTENT_TYPE", MimetypesFileTypeMap.getDefaultFileTypeMap().getContentType(fi.getName()));
formInput.put(fi.getFieldName() + "_CONTENT_NAME", fi.getName());
formInput.put(fi.getFieldName() + "_CONTENT_SIZE", fi.getSize());
}
formInput.put(fi.getFieldName(), content == null ? null : new ByteArrayInputStream(content));
}
}
} catch (FileUploadException e1) {
throw new RuntimeException(e1);
} catch (IOException e1){
throw new RuntimeException(e1);
}
}else {
Enumeration<String> parameterNames = request.getParameterNames();
while (parameterNames.hasMoreElements()){
String name =parameterNames.nextElement();
formInput.put(name,request.getParameter(name));
}
}
for (String key : formInput.keySet()){
int dotIndex = key.indexOf('.') ;
if ( dotIndex < 0){
this.formFields.put(key, formInput.get(key));
}else{
String fieldName = key.substring(dotIndex+1);
String modelNameWithIndex = key.substring(0,dotIndex);
int indexOfOpenBracket = modelNameWithIndex.lastIndexOf('[');
int indexOfCloseBracket = modelNameWithIndex.indexOf(']',indexOfOpenBracket);
String modelName = modelNameWithIndex.substring(0, indexOfOpenBracket);
Integer index = Integer.valueOf(modelNameWithIndex.substring(indexOfOpenBracket+1,indexOfCloseBracket));
SortedMap<Integer,Map<String,Object>> modelRecords = (SortedMap<Integer, Map<String, Object>>) formFields.get(modelName);
if (modelRecords == null){
modelRecords = new TreeMap<Integer,Map<String,Object>>();
formFields.put(modelName, modelRecords);
}
Map<String,Object> modelAttributes = modelRecords.get(index);
if (modelAttributes == null){
modelAttributes = new HashMap<String,Object>();
modelRecords.put(index, modelAttributes);
}
modelAttributes.put(fieldName, formInput.get(key));
}
}
return formFields;
}
public User getSessionUser(){
HttpSession session = getSession();
if (session == null){
return null;
}
_Identifiable user = null;
try {
user = (_Identifiable)session.getAttribute("user");
return (User)user;
}catch (ClassCastException ex){
user = null;
session.removeAttribute("user");
Integer id = (Integer)session.getAttribute("user.id");
if (id != null){
Table<User> USER = Database.getTable(User.class);
if (USER != null){
user = USER.get(id);
getSession().setAttribute("user", user);
}
}
return (User)user;
}
}
public Integer getSessionUserId(){
if (getSession() == null){
return null;
}
Integer id = (Integer)getSession().getAttribute("user.id");
return id;
}
public HttpSession getSession() {
return session;
}
public void setSession(HttpSession session) {
this.session = session;
}
public HttpServletRequest getRequest() {
return request;
}
private static final String ORIGNAL_REQUEST_KEY = "swf.original.request.uri";
public void setRequest(HttpServletRequest request) {
this.request = request;
Object originalUrl = request.getAttribute(ORIGNAL_REQUEST_KEY);
if (originalUrl == null){
request.setAttribute(ORIGNAL_REQUEST_KEY,request.getRequestURI());
}
}
public String getOriginalRequestUrl(){
return StringUtil.valueOf(request.getAttribute(ORIGNAL_REQUEST_KEY));
}
protected void logHeaders(){
if (request != null){
List<String> headers = new ArrayList<String>();
Enumeration<String> names = request.getHeaderNames();
while(names.hasMoreElements()){
headers.add(names.nextElement());
}
Config.instance().getLogger(Path.class.getName()).info("Request Headers:" + headers.toString());
}
}
public HttpServletResponse getResponse() {
return response;
}
public void setResponse(HttpServletResponse response) {
this.response = response;
}
public Path constructNewPath(String target){
Path p = new Path(target);
p.setSession(getSession());
p.setRequest(getRequest());
p.setResponse(getResponse());
return p;
}
public Path(String target) {
this.target = target;
StringTokenizer stok = new StringTokenizer(target, "/");
StringBuilder resourcePath = new StringBuilder();
boolean isResource = false;
while (stok.hasMoreTokens()) {
String token = stok.nextToken();
if (pathelements.isEmpty() && token.equals("resources")){
isResource = true;
}else if (isResource){
resourcePath.append("/").append(token);
}
pathelements.add(token);
}
if (isResource){
if (!ObjectUtil.isVoid(resourcePath.toString())){
try {
URL resource = getClass().getResource(resourcePath.toString());
if (resource == null){
isResource = false;
}
}catch (Exception ex){
isResource = false;
}
}else {
isResource = false;
}
if (isResource){
pathelements.clear();
pathelements.add("resources");
pathelements.add(resourcePath.toString());
}
}
int pathElementSize = pathelements.size();
for (int i = 0 ; !isResource && i < pathElementSize ; i++){
String token = pathelements.get(i);
try {
Integer.valueOf(token);
continue; //Ignore integer elements;
}catch (NumberFormatException e){
//
}
String controllerClassName = ControllerCache.instance().get(token);
if (controllerClassName == null){
continue;
}
ControllerInfo info = new ControllerInfo(token,controllerClassName);
info.setControllerPathIndex(i);
controllerElements.add(info);
if (i < pathElementSize -1){
info.setAction(pathelements.get(i+1));
i+= 1;
}
try {
if (i < pathElementSize - 1){
info.setParameter(Integer.valueOf(pathelements.get(i+1)));
i+=1;
}
}catch (NumberFormatException ex){
//It is possible that the parameter is a string one. So lets check the next parameter to see if it is a model or this is
//the last pathelement.
if (i < pathElementSize - 2){
String nextElement = pathelements.get(i+2);
try {
Integer.valueOf(nextElement);
}catch(NumberFormatException nfe){
if (ControllerCache.instance().get(nextElement) != null){
info.setParameter(pathelements.get(i+1));
i ++ ;
}
}
}else {
//Last pathelement.
info.setParameter(pathelements.get(i+1));
i++;
}
}
}
if (pathElementSize == 0){
pathelements.add("index");
}
loadControllerClassName(isResource);
}
public static class ControllerInfo {
private String action = null;
private Object parameter = null;
private Class<? extends Model> modelClass = null;
private Class<?> controllerClass = null;
private int controllerPathIndex = -1;
public ControllerInfo(String controllerName, String controllerClassName){
this.controllerClass = Path.getClass(controllerClassName);
this.modelClass = Path.getModelClass(controllerName);
}
public Class<?> getControllerClass() {
return controllerClass;
}
public Class<? extends Model> getModelClass(){
return modelClass;
}
public String getAction() {
return action;
}
public void setAction(String action) {
this.action = action;
}
@SuppressWarnings("unchecked")
public <T> T getParameter() {
return (T) parameter;
}
public <T> void setParameter(T parameter) {
this.parameter = parameter;
}
public Integer getId() {
if (parameter == null){
return null;
}
if (parameter instanceof Integer){
return (Integer)parameter;
}
return null;
}
public int getControllerPathIndex() {
return controllerPathIndex;
}
public void setControllerPathIndex(int controllerPathIndex) {
this.controllerPathIndex = controllerPathIndex;
}
}
private List<ControllerInfo> controllerElements = new ArrayList<Path.ControllerInfo>();
public List<ControllerInfo> getControllerElements(){
return controllerElements;
}
public String getTarget() {
return target;
}
private void loadControllerClassName(boolean isResource){
if (controllerClassName != null){
return;
}
if (!controllerElements.isEmpty()){
ControllerInfo cinfo = controllerElements.get(controllerElements.size() -1 );
controllerClassName = cinfo.getControllerClass().getName();
controllerPathIndex = cinfo.getControllerPathIndex();
}
if (controllerClassName == null) {
controllerClassName = Controller.class.getName();
controllerPathIndex = -1;
}
actionPathIndex = controllerPathIndex + 1 ;
parameterPathIndex = controllerPathIndex + 2;
}
public String controllerPath(){
if (controllerPathIndex <= pathelements.size() -1){
StringBuilder p = new StringBuilder();
for (int i = 0; i<= controllerPathIndex ; i ++){
p.append("/");
p.append(pathelements.get(i));
}
return p.toString();
}
throw new RuntimeException("Controller path could not be determined!");
}
public String getBackTarget(){
return getBackTarget(true);
}
private String getBackTarget(boolean checkIfOrigRequest){
StringBuilder backTarget = new StringBuilder();
if (checkIfOrigRequest && !getTarget().equals(getOriginalRequestUrl())){
Path p = constructNewPath(getOriginalRequestUrl());
return p.getBackTarget();
}else {
if (controllerPathIndex > 0 && controllerPathIndex < pathelements.size()) {
for (int i = 0 ; i < controllerPathIndex ; i ++ ){
backTarget.append("/");
backTarget.append(pathelements.get(i));
}
}
if (backTarget.length() == 0){
backTarget.setLength(0);
backTarget.append(controllerPath()).append("/index");
return backTarget.toString();
}
}
String url = backTarget.toString();
if (url.endsWith("/index")){ // http://host:port/..../controller/index
Path backPath = constructNewPath(url);
if (backPath.getModelClass() != null){
Path twobackPath = constructNewPath(backPath.getBackTarget(false));
if (twobackPath.getModelClass() != null){
ModelReflector<? extends Model> bmr = ModelReflector.instance(twobackPath.getModelClass());
for (Class<? extends Model> childModel : bmr.getChildModels(true, true)){
if (ModelReflector.instance(childModel).reflects(backPath.getModelClass())){
url = twobackPath.getTarget() + "?_select_tab="+ StringEscapeUtils.escapeHtml4(new ModelAwareness(backPath, null).getLiteral(backPath.getModelClass().getSimpleName()));
break;
}
}
}
}
}
return url;
}
public String controllerPathElement(){
if (controllerPathIndex <= pathelements.size() - 1){
if (controllerPathIndex >= 0){
return pathelements.get(controllerPathIndex);
}else {
return "";
}
}
throw new RuntimeException("Controller pathelement could not be determined!");
}
public <M extends Model> Class<M> getModelClass(){
return getModelClass(controllerPathElement());
}
public static <M extends Model> Class<M> getModelClass(String pathElement){
Table<M> table = getTable(pathElement);
if (table == null){
return null;
}else {
return table.getModelClass();
}
}
public static <M extends Model> Table<M> getTable(String pathElement){
String camelPathElement = StringUtil.camelize(pathElement);
if (StringUtil.pluralize(camelPathElement).equals(camelPathElement)){
String tableName = Table.tableName(StringUtil.singularize(camelPathElement));
Table<M> table = Database.getTable(tableName);
return table;
}
return null;
}
public String action() {
String action = "index";
if (actionPathIndex <= pathelements.size() - 1) {
action = pathelements.get(actionPathIndex);
}
return action;
}
public String parameter() {
String parameter = null;
if (parameterPathIndex <= pathelements.size() - 1) {
parameter = pathelements.get(parameterPathIndex);
}
return parameter;
}
private Controller createController() {
try {
return (Controller)getControllerClass().getConstructor(Path.class).newInstance(this);
} catch (NoSuchMethodException ex) {
throw new RuntimeException(ex);
} catch (SecurityException ex) {
throw new RuntimeException(ex);
}catch (InstantiationException ex) {
throw new RuntimeException(ex);
} catch (IllegalAccessException ex) {
throw new RuntimeException(ex);
} catch (IllegalArgumentException ex) {
throw new RuntimeException(ex);
} catch (InvocationTargetException ex) {
throw new RuntimeException(ex.getCause());
}
}
public static final String ALLOW_CONTROLLER_ACTION = "allow.controller.action" ;
public boolean isUserLoggedOn(){
User currentUser = Database.getInstance().getCurrentUser();
if (currentUser == null){
User sessionUser = getSessionUser();
if (sessionUser != null){
Database.getInstance().open(sessionUser);
}
currentUser = Database.getInstance().getCurrentUser();
}
return currentUser != null ;
}
public void createUserSession(User user,boolean autoInvalidate){
HttpSession session = getRequest().getSession(true);
session.setAttribute("user", user);
session.setAttribute("user.id",user.getId());
session.setAttribute("autoInvalidate", autoInvalidate);
setSession(session);
}
public boolean isRequestAuthenticated(){
if (isUserLoggedOn()){
return true;
}
User user = null;
boolean autoInvalidate = false;
String apiKey = getRequest().getHeader("ApiKey");
if (ObjectUtil.isVoid(apiKey)){
apiKey = getRequest().getParameter("ApiKey");
}
if (!ObjectUtil.isVoid(apiKey)){
autoInvalidate = true;
user = getUser("api_key",apiKey);
}
if (user == null){
if (getRequest().getMethod().equalsIgnoreCase("POST")){
String username = getRequest().getParameter("name");
if (!ObjectUtil.isVoid(username)){
Config.instance().getLogger(Path.class.getName()).fine("Logging in " + username);
user = getUser("name",username);
Config.instance().getLogger(Path.class.getName()).fine("User is valid ? " + (user != null));
String password = getRequest().getParameter("password");
if (user != null && user.authenticate(password)){
createUserSession(user,autoInvalidate);
}else {
Config.instance().getLogger(Path.class.getName()).fine("Authentication Failed");
}
}
}
}else {
createUserSession(user,autoInvalidate);
}
return isUserLoggedOn();
}
public MimeType getProtocol(){
String apiprotocol = getRequest().getHeader("ApiProtocol");
return Path.getProtocol(apiprotocol);
}
public static MimeType getProtocol(String apiprotocol){
if (ObjectUtil.isVoid(apiprotocol)){
return MimeType.TEXT_HTML;
}
MimeType protocol = MimeType.TEXT_HTML;
for (MimeType mt : MimeType.values()){
if (mt.toString().equals(apiprotocol)){
protocol = mt;
break;
}
}
return protocol;
}
//Can be cast to any user model class as the proxy implements all the user classes.
protected User getUser(String fieldName, String fieldValue){
Select q = new Select().from(User.class);
String nameColumn = ModelReflector.instance(User.class).getColumnDescriptor(fieldName).getName();
q.where(new Expression(q.getPool(),nameColumn,Operator.EQ,new BindVariable(q.getPool(),fieldValue)));
List<? extends User> users = q.execute(User.class);
if (users.size() == 1){
return users.get(0);
}
return null;
}
public User getGuestUser(){
String guestUserName = Config.instance().getProperty("swf.guest.user");
if (!ObjectUtil.isVoid(guestUserName)){
Select userSelect = new Select().from(User.class);
List<User> guests = userSelect.where(new Expression(userSelect.getPool(),"NAME",Operator.EQ,guestUserName)).execute(User.class);
if (guests.size() == 1){
return guests.get(0);
}
}
return null;
}
public boolean isGuestUserLoggedOn(){
String guestUserName = Config.instance().getProperty("swf.guest.user");
User u = getSessionUser();
if (u != null && ObjectUtil.equals(u.getName(),guestUserName)){
return true;
}
return false;
}
private final SWFLogger cat = Config.instance().getLogger(getClass().getName());
public _IView invoke() throws AccessDeniedException{
MultiException ex = null;
List<Method> methods = getActionMethods(action(), parameter());
for (Method m :methods){
Timer timer = cat.startTimer(null,Config.instance().isTimerAdditive());
try {
boolean securedAction = getControllerReflector().isSecuredActionMethod(m) ;
if (securedAction){
if (!isRequestAuthenticated()){
User guest = getGuestUser();
if (guest != null){
createUserSession(guest, false);
}
if(!isRequestAuthenticated()) {
if (getProtocol() == MimeType.TEXT_HTML){
return new RedirectorView(this,"","login");
}else {
throw new RuntimeException ("Request not authenticated");
}
}
}
ensureControllerActionAccess();
}
Controller controller = createController();
try {
if (m.getParameterTypes().length == 0 && parameter() == null){
return (View)m.invoke(controller);
}else if (m.getParameterTypes().length == 1 && m.getParameterTypes()[0] == String.class && parameter() != null){
return (View)m.invoke(controller, parameter());
}else if (m.getParameterTypes().length == 1 && m.getParameterTypes()[0] == int.class && parameter() != null){
return (View)m.invoke(controller, Integer.valueOf(parameter()));
}
}catch(Exception e){
e.printStackTrace();
if (ex == null){
ex = new MultiException();
}
ex.add(e);
}
}finally{
timer.stop();
}
}
if (ex != null){
throw ex;
}
if (!isUserLoggedOn()){
return new RedirectorView(this,"","login");
}
throw new RuntimeException("Donot know how to invoke controller action" + getTarget()) ;
}
public boolean canAccessControllerAction(){
return canAccessControllerAction(action());
}
public boolean canAccessControllerAction(String actionPathElement){
return canAccessControllerAction(actionPathElement,parameter());
}
private ControllerReflector<? extends Controller> cref = null;
public ControllerReflector<? extends Controller> getControllerReflector(){
if (cref == null){
cref = ControllerReflector.instance(getControllerClass());
}
return cref;
}
public boolean isActionSecure(String actionPathElement){
return getControllerReflector().isActionSecure(actionPathElement);
}
private List<Method> getActionMethods(final String actionPathElement,final String parameterPathElement){
List<Method> methods = getControllerReflector().getActionMethods(actionPathElement);
final int targetParameterLength = ObjectUtil.isVoid(parameterPathElement)? 0 : 1;
boolean parameterIsNumeric = false;
if (targetParameterLength == 1) {
try {
Double.parseDouble(parameterPathElement);
parameterIsNumeric = true;
}catch (NumberFormatException nfex){
//
}
}
final Class<?> targetParameterType = targetParameterLength == 0 ? null : (parameterIsNumeric ? int.class : String.class);
Collections.sort(methods,new Comparator<Method>(){
public int compare(Method o1, Method o2) {
int ret = 0 ;
int s1 = 0 ; int s2 = 0 ;
s1 = Math.abs(o1.getParameterTypes().length - targetParameterLength);
s2 = Math.abs(o2.getParameterTypes().length - targetParameterLength) ;
ret = s1 - s2;
if (ret == 0 && o1.getParameterTypes().length == 1){
s1 = o1.getParameterTypes()[0].equals(targetParameterType) ? 0 : 1;
s2 = o2.getParameterTypes()[0].equals(targetParameterType) ? 0 : 1;
ret = s1 - s2;
}
return ret;
}
});
return methods;
}
public boolean canAccessControllerAction(String actionPathElement,String parameterPathElement){
boolean accessible = canAccessControllerAction(getSessionUser(), controllerPathElement(), actionPathElement, parameterPathElement,this);
if (!accessible) {
return accessible;
}
List<Method> methods = getActionMethods(actionPathElement, parameterPathElement);
for (Method m: methods){
accessible = false;
Depends depends = getControllerReflector().getAnnotation(m,Depends.class);
if (depends != null ){
accessible = true;
StringTokenizer tok = new StringTokenizer(depends.value(),",") ;
while (tok.hasMoreTokens() && accessible){
accessible = accessible && canAccessControllerAction(tok.nextToken(),parameterPathElement);
}
}else {
accessible = true ;
}
if (accessible){
break ;
}
}
return accessible;
}
public static boolean canAccessControllerAction(User user,String controllerPathElement,String actionPathElement,String parameterPathElement,Path path){
try {
ensureControllerActionAccess(user,controllerPathElement,actionPathElement,parameterPathElement,path);
}catch (AccessDeniedException ex){
return false;
}
return true;
}
private void ensureControllerActionAccess() throws AccessDeniedException{
ensureControllerActionAccess(getSessionUser(),controllerPathElement(),action(),parameter(),this);
}
private static void ensureControllerActionAccess(User user,String controllerPathElement,String actionPathElement , String parameterPathElement,Path path) throws AccessDeniedException{
Registry.instance().callExtensions(ALLOW_CONTROLLER_ACTION, user, controllerPathElement,actionPathElement,parameterPathElement, path);
}
@SuppressWarnings("unchecked")
public <T extends Controller> Class<T> getControllerClass() {
return (Class<T>) getClass(getControllerClassName());
}
public static Class<?> getClass(String name) {
try {
return Class.forName(name);
} catch (ClassNotFoundException ex) {
return null;
}
}
private String getControllerClassName() {
return controllerClassName;
}
public Path createRelativePath(String toUrl){
String relPath = null;
if (parameter() != null){
relPath = getTarget();
}else {
relPath = controllerPath() ;
}
if (!toUrl.startsWith("/")){
relPath = relPath + "/" + toUrl;
}else {
relPath = relPath + toUrl;
}
Path path = constructNewPath(relPath);
return path;
}
private List<Method> getReferredModelGetters(Map<String,List<Method>> referredModelGettersMap, String referredTableName){
List<Method> rmg = referredModelGettersMap.get(referredTableName);
if (rmg == null){
rmg = new ArrayList<Method>();
}
return rmg;
}
public Expression getWhereClause(){
return getWhereClause(getModelClass());
}
public Expression getWhereClause(Class<? extends Model> modelClass){
Map<String, List<Method>> referredModelGetterMap = new HashMap<String, List<Method>>();
ModelReflector<? extends Model> reflector = ModelReflector.instance(modelClass);
Expression where = new Expression(reflector.getPool(),Conjunction.AND);
for (Method referredModelGetter : reflector.getReferredModelGetters()){
@SuppressWarnings("unchecked")
Class<? extends Model> referredModelClass = (Class<? extends Model>) referredModelGetter.getReturnType();
ModelReflector<? extends Model> referredModelReflector = ModelReflector.instance(referredModelClass);
String referredTableName = referredModelReflector.getTableName();
List<Method> getters = referredModelGetterMap.get(referredTableName);
if (getters == null){
getters = new ArrayList<Method>();
referredModelGetterMap.put(referredTableName, getters);
}
getters.add(referredModelGetter);
}
if (referredModelGetterMap.isEmpty()){
return where;
}
List<ControllerInfo> controllerElements = new ArrayList<ControllerInfo>(getControllerElements());
Collections.reverse(controllerElements);
Iterator<ControllerInfo> cInfoIter = controllerElements.iterator() ;
if (cInfoIter.hasNext()){
cInfoIter.next();// The last model was self.
}
Set<String> modelElementProcessed = new HashSet<String>();
while(cInfoIter.hasNext()){
ControllerInfo controllerInfo = cInfoIter.next();
if (controllerInfo.getModelClass() == null){
continue;
}
ModelReflector<? extends Model> ref = ModelReflector.instance(controllerInfo.getModelClass());
List<Method> referredModelGetters = getReferredModelGetters(referredModelGetterMap, ref.getTableName());
if (referredModelGetters.isEmpty() || controllerInfo.getId() == null || modelElementProcessed.contains(ref.getTableName())){
continue;
}
modelElementProcessed.add(ref.getTableName());
Expression referredModelWhere = new Expression(ref.getPool(),Conjunction.AND);
Expression referredModelWhereChoices = new Expression(ref.getPool(),Conjunction.OR);
ModelReflector<?> referredModelReflector = ref;
for (Method childGetter : referredModelReflector.getChildGetters()){
Class<? extends Model> childModelClass = referredModelReflector.getChildModelClass(childGetter);
if (reflector.getClassHierarchies().contains(childModelClass)){
CONNECTED_VIA join = referredModelReflector.getAnnotation(childGetter,CONNECTED_VIA.class);
if (join == null){
for (Method referredModelGetter: referredModelGetters){
String referredModelIdFieldName = reflector.getReferenceField(referredModelGetter);
String referredModelIdColumnName = reflector.getColumnDescriptor(referredModelIdFieldName).getName();
referredModelWhereChoices.add(new Expression(referredModelReflector.getPool(),referredModelIdColumnName,Operator.EQ,new BindVariable(referredModelReflector.getPool(),controllerInfo.getId())));
}
}else {
String referredModelIdColumnName = join.value();
referredModelWhereChoices.add(new Expression(referredModelReflector.getPool(),referredModelIdColumnName,Operator.EQ,new BindVariable(referredModelReflector.getPool(),controllerInfo.getId())));
if (!ObjectUtil.isVoid(join.additional_join())){
SQLExpressionParser parser = new SQLExpressionParser(modelClass);
Expression expression = parser.parse(join.additional_join());
referredModelWhere.add(expression);
}
}
}
}
if (!referredModelWhereChoices.isEmpty()){
referredModelWhere.add(referredModelWhereChoices);
}
if (referredModelWhere.getParameterizedSQL().length() > 0){
where.add(referredModelWhere);
}
}
User user = getSessionUser();
if (user != null){
Cache<String,Map<String,List<Integer>>> pOptions = user.getParticipationOptions(reflector.getModelClass());
if (pOptions.size() > 0){
Set<String> fields = new HashSet<String>();
for (String g: pOptions.keySet()){
fields.addAll(pOptions.get(g).keySet());
}
fields.removeAll(DataSecurityFilter.getRedundantParticipationFields(fields, reflector));
boolean canFilterInSQL = !DataSecurityFilter.anyFieldIsVirtual(fields, reflector);
if (canFilterInSQL){
Expression dsw = user.getDataSecurityWhereClause(reflector,pOptions);
if (dsw.getParameterizedSQL().length()> 0){
where.add(dsw);
}
}
}
}
return where;
}
@Override
public void invalidateSession() {
if (session != null){
session.invalidate();
session = null;
}
}
public void autoInvalidateSession(){
if (session != null){
try {
if (ObjectUtil.equals(session.getAttribute("autoInvalidate"),true)){
invalidateSession();
}
}catch(IllegalStateException ex){
session = null;
}
}
}
public static final String getControllerPathElementName(Class<? extends Model> modelClass){
return LowerCaseStringCache.instance().get(Database.getTable(modelClass).getTableName());
}
public <M extends Model> Path getModelAccessPath(Class<M> modelClass){
return pathCache.get(modelClass);
}
private Cache<Class<? extends Model>, Path> pathCache = new Cache<Class<? extends Model>, Path>() {
/**
*
*/
private static final long serialVersionUID = -1430185913473112366L;
@Override
protected Path getValue(Class<? extends Model> modelClass) {
Path p = Path.this;
if (!p.controllerPathElement().equals(getControllerPathElementName(modelClass))){
Path newPath = constructNewPath("/" + getControllerPathElementName(modelClass) + "/index");
p = newPath;
}
return p;
}
};
public <M extends Model> void fillDefaultsForReferenceFields(M record,Class<M> modelClass){
List<ControllerInfo> controllerElements = new ArrayList<ControllerInfo>(getControllerElements());
Collections.reverse(controllerElements);
ModelReflector<M> reflector = ModelReflector.instance(modelClass);
for (Method referredModelGetter: reflector.getReferredModelGetters()){
@SuppressWarnings("unchecked")
Class<? extends Model> referredModelClass = (Class<? extends Model>)referredModelGetter.getReturnType();
String referredModelIdFieldName = reflector.getReferenceField(referredModelGetter);
if (!reflector.isFieldSettable(referredModelIdFieldName) || reflector.isHouseKeepingField(referredModelIdFieldName)){
continue;
}
Method referredModelIdSetter = reflector.getFieldSetter(referredModelIdFieldName);
Method referredModelIdGetter = reflector.getFieldGetter(referredModelIdFieldName);
try {
Integer oldValue = (Integer) referredModelIdGetter.invoke(record);
if (!Database.getJdbcTypeHelper(reflector.getPool()).isVoid(oldValue)){
continue;
}
Integer valueToSet = null;
Iterator<ControllerInfo> miIter = controllerElements.iterator();
if (miIter.hasNext()){
miIter.next();
//Last model was self so ignore the first one now as model Elements is already reversed.
}
while (miIter.hasNext()){
ControllerInfo mi = miIter.next();
if (mi.getId() == null){
continue;
}
if (mi.getModelClass() == null){
continue;
}
ModelReflector<? extends Model> ref = ModelReflector.instance(mi.getModelClass());
if (ref.reflects(referredModelClass)){
try {
Model referredModel = Database.getTable(referredModelClass).get(mi.getId());
if (referredModel.isAccessibleBy(getSessionUser(),referredModelClass)){
valueToSet = mi.getId();
break;
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
if (valueToSet == null){
List<Integer> idoptions = null ;
PARTICIPANT participant = reflector.getAnnotation(referredModelIdGetter, PARTICIPANT.class);
if (participant != null && participant.defaultable()){
idoptions = getSessionUser().getParticipationOptions(modelClass).get(participant.value()).get(referredModelIdFieldName);
}
if (idoptions != null && !idoptions.isEmpty()){
if (idoptions.size() == 1){
valueToSet = idoptions.get(0);
}else if (idoptions.size() == 2 && idoptions.contains(null)){
for (Integer i:idoptions){
if (i != null){
valueToSet = i;
}
}
}
}
}
if (valueToSet != null){
Model referredModel = Database.getTable(referredModelClass).get(valueToSet);
if (referredModel.isAccessibleBy(getSessionUser(),referredModelClass)){
referredModelIdSetter.invoke(record,valueToSet);
}
}
} catch (Exception e1) {
throw new RuntimeException(e1);
}
}
}
public void addMessage(StatusType type, String message){
if (getSession() == null){
return ;
}
HttpSession session = getSession();
@SuppressWarnings("unchecked")
List<String> existing = (List<String>) session.getAttribute(type.getSessionKey());
if (existing == null){
existing = new SequenceSet<String>();
session.setAttribute(type.getSessionKey(), existing);
}
if (!ObjectUtil.isVoid(message)){
existing.add(message);
}
}
public List<String> getMessages(StatusType type){
SequenceSet<String> ret = new SequenceSet<String>();
if (getSession() == null){
return ret;
}
HttpSession session = getSession();
@SuppressWarnings("unchecked")
List<String> existing = (List<String>) session.getAttribute(type.getSessionKey());
if (existing != null){
ret.addAll(existing);
existing.clear();
}
return ret;
}
@Override
public void addErrorMessage(String msg) {
addMessage(StatusType.ERROR, msg);
}
@Override
public void addInfoMessage(String msg) {
addMessage(StatusType.INFO, msg);
}
@Override
public List<String> getErrorMessages() {
return getMessages(StatusType.ERROR);
}
@Override
public List<String> getInfoMessages() {
return getMessages(StatusType.INFO);
}
public boolean isForwardedRequest(){
return !ObjectUtil.isVoid(getRequest().getAttribute("javax.servlet.forward.request_uri"));
}
}