/** * Copyright (c) 2015, Lucee Assosication Switzerland. All rights reserved. * * 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, see <http://www.gnu.org/licenses/>. * */ package lucee.runtime; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Set; import lucee.commons.lang.ExceptionUtil; import lucee.runtime.component.DataMember; import lucee.runtime.component.Member; import lucee.runtime.debug.DebugEntryTemplate; import lucee.runtime.dump.DumpData; import lucee.runtime.dump.DumpProperties; import lucee.runtime.dump.DumpTable; import lucee.runtime.dump.DumpUtil; import lucee.runtime.dump.SimpleDumpData; import lucee.runtime.engine.ThreadLocalPageContext; import lucee.runtime.exp.ExpressionException; import lucee.runtime.exp.PageException; import lucee.runtime.type.Collection; import lucee.runtime.type.Objects; import lucee.runtime.type.Struct; import lucee.runtime.type.UDFPlus; import lucee.runtime.type.scope.Variables; import lucee.runtime.type.util.KeyConstants; import lucee.runtime.type.util.StructSupport; public class StaticScope extends StructSupport implements Variables,Objects { private static final long serialVersionUID = -2692540782121852340L; private final StaticScope base; private ComponentPageImpl cp; private final int dataMemberDefaultAccess; private final ComponentImpl c; public StaticScope(StaticScope base, ComponentImpl c,ComponentPageImpl cp, int dataMemberDefaultAccess) { this.base=base; this.cp=cp; this.c=c; this.dataMemberDefaultAccess=dataMemberDefaultAccess; } public PageSource getPageSource() { return this.cp.getPageSource(); } @Override public int size() { if(base==null) return cp._static.size(); return base.size()+cp._static.size(); } public Member _remove(PageContext pc,Key key) throws PageException { // does the current struct has this key Member m = cp._static.get(key); if(m!=null) { if(m.getModifier()==Member.MODIFIER_FINAL) throw new ExpressionException("Cannot remove key ["+key+"] in static scope from component ["+cp.getComponentName()+"], that member is set to final"); if(!c.isAccessible(ThreadLocalPageContext.get(pc), m.getAccess())) throw new ExpressionException("Component from type ["+cp.getComponentName()+"] has no accessible static Member with name ["+key+"]"); return cp._static.remove(key); } // if not the parent (inside the static constructor we do not remove keys from base static scopes) if(base!=null && !c.insideStaticConstr) return base._remove(pc,key); return null; } @Override public Object remove(Key key) throws PageException { Member m = _remove(ThreadLocalPageContext.get(),key); if(m!=null) return m.getValue(); return null; } @Override public Object removeEL(Key key) { try{ return remove(key); }catch(Throwable t){ExceptionUtil.rethrowIfNecessary(t);return null;} } @Override public void clear() { if(base!=null) base.clear(); cp._static.clear(); } private Member _get(PageContext pc,Key key, Member defaultValue) { // does the current struct has this key Member m = cp._static.get(key); if(m!=null) { if(c.isAccessible(pc,m)) return m; return null; } // if not the parent if(base!=null) return base._get(pc,key,defaultValue); return null; } @Override public Object get(Key key) throws PageException { return get((PageContext)null,key); } @Override public Object get(PageContext pc, Key key) throws PageException { if(key.equalsIgnoreCase(KeyConstants._STATIC)) return c.top._static; Member m = _get(ThreadLocalPageContext.get(pc),key, null); if(m!=null) return m.getValue(); throw new ExpressionException("Component from type ["+cp.getComponentName()+"] has no accessible static Member with name ["+key+"]"); } @Override public Object get(Key key, Object defaultValue) { return get(null,key,defaultValue); } @Override public Object get(PageContext pc, Key key, Object defaultValue) { if(key.equalsIgnoreCase(KeyConstants._STATIC)) return c.top._static; Member m = _get(ThreadLocalPageContext.get(pc),key, null); if(m!=null) return m.getValue(); return defaultValue; } private Member _setIfExists(PageContext pc, Key key, Object value) throws PageException { // does the current struct has this key Member m = cp._static.get(key); if(m!=null) { if(m.getModifier()==Member.MODIFIER_FINAL) throw new ExpressionException("Cannot update key ["+key+"] in static scope from component ["+cp.getComponentName()+"], that member is set to final"); return _set(pc,m, key, value); } // if not the parent (we only do this if we are outside the static constructor) if(base!=null && !c.insideStaticConstr) return base._setIfExists(pc,key, value); return null; } private Member _set(PageContext pc, Member existing, Key key, Object value) throws ExpressionException { if(value instanceof Member) return cp._static.put(key, (Member)value); // check if user has access if(!c.isAccessible(pc, existing!=null?existing.getAccess():dataMemberDefaultAccess)) throw new ExpressionException("Component from type ["+cp.getComponentName()+"] has no accessible static Member with name ["+key+"]"); // set return cp._static.put(key,new DataMember(existing!=null?existing.getAccess():dataMemberDefaultAccess,existing!=null?existing.getModifier():Member.MODIFIER_NONE,value)); } @Override public Object set(Key propertyName, Object value) throws PageException { return set(null,propertyName, value); } @Override public Object set(PageContext pc, Key key, Object value) throws PageException { pc = ThreadLocalPageContext.get(pc); Member m = _setIfExists(pc,key, value); if(m!=null) return m.getValue(); // if not exists set to current m= _set(pc,null, key, value); if(m!=null) return m.getValue(); return null; } @Override public Object setEL(Key key, Object value) { return setEL(null, key, value); } @Override public Object setEL(PageContext pc, Key propertyName, Object value) { try {return set(ThreadLocalPageContext.get(pc),propertyName, value);} catch (PageException e) {return null;} } @Override public Collection duplicate(boolean deepCopy) { return this; } @Override public boolean containsKey(Key key) { if(base!=null && base.containsKey(key)) return true; return cp._static.containsKey(key); } @Override public Key[] keys() { Set<Key> keys = _entries(new HashMap<Key,Object>(),c.getAccess(ThreadLocalPageContext.get())).keySet(); return keys.toArray(new Key[keys.size()]); } @Override public Iterator<Key> keyIterator() { return _entries(new HashMap<Key,Object>(),c.getAccess(ThreadLocalPageContext.get())).keySet().iterator(); } @Override public Iterator<Object> valueIterator() { return _entries(new HashMap<Key,Object>(),c.getAccess(ThreadLocalPageContext.get())).values().iterator(); } @Override public Iterator<Entry<Key, Object>> entryIterator() { return _entries(new HashMap<Key, Object>(),c.getAccess(ThreadLocalPageContext.get())).entrySet().iterator(); } private Map<Key, Object> _entries(Map<Key, Object> map, int access) { // call parent if(base!=null) base._entries(map,access); // fill accessable keys Iterator<Entry<Key, Member>> it = cp._static.entrySet().iterator(); Entry<Key, Member> e; while(it.hasNext()){ e = it.next(); if(e.getValue().getAccess()<=access) map.put(e.getKey(),e.getValue().getValue()); } return map; } private Map<Key, Member> all(Map<Key, Member> map) { // call parent if(base!=null) base.all(map); // fill accessable keys Iterator<Entry<Key, Member>> it = cp._static.entrySet().iterator(); Entry<Key, Member> e; while(it.hasNext()){ e = it.next(); map.put(e.getKey(),e.getValue()); } return map; } @Override public Object call(PageContext pc, Key key, Object[] args) throws PageException { Member m = _get(pc, key, null); if(m instanceof UDFPlus){ return _call(pc,key,((UDFPlus)m),null,args); } throw new ExpressionException("Component from type ["+cp.getComponentName()+"] has no accessible static Member with name ["+key+"]"); } @Override public Object callWithNamedValues(PageContext pc, Key key,Struct args) throws PageException { Member m = _get(pc, key, null); if(m instanceof UDFPlus){ return _call(pc,key,((UDFPlus)m),args,null); } throw new ExpressionException("Component from type ["+cp.getComponentName()+"] has no accessible static Member with name ["+key+"]"); } Object _call(PageContext pc, Collection.Key calledName,UDFPlus udf, Struct namedArgs, Object[] args) throws PageException { Object rtn=null; Variables parent=null; // INFO duplicate code is for faster execution -> less contions // debug yes if(pc.getConfig().debug()) { DebugEntryTemplate debugEntry=pc.getDebugger().getEntry(pc,cp.getPageSource(),udf.getFunctionName());//new DebugEntry(src,udf.getFunctionName()); long currTime=pc.getExecutionTime(); long time=System.nanoTime(); // sync yes if(c.top.properties._synchronized){ synchronized (this) { try { parent=c.beforeStaticConstructor(pc); if(args!=null)rtn=udf.call(pc,calledName,args,true); else rtn=udf.callWithNamedValues(pc,calledName,namedArgs,true); } finally { c.afterStaticConstructor(pc, parent); long diff= ((System.nanoTime()-time)-(pc.getExecutionTime()-currTime)); pc.setExecutionTime(pc.getExecutionTime()+diff); debugEntry.updateExeTime(diff); } } } // sync no else { try { parent=c.beforeStaticConstructor(pc); if(args!=null)rtn=udf.call(pc,calledName,args,true); else rtn=udf.callWithNamedValues(pc,calledName,namedArgs,true); } finally { c.afterStaticConstructor(pc, parent); long diff= ((System.nanoTime()-time)-(pc.getExecutionTime()-currTime)); pc.setExecutionTime(pc.getExecutionTime()+diff); debugEntry.updateExeTime(diff); } } } // debug no else { // sync yes if(c.top.properties._synchronized){ synchronized (this) { try { parent=c.beforeStaticConstructor(pc); if(args!=null)rtn=udf.call(pc,calledName,args,true); else rtn=udf.callWithNamedValues(pc,calledName,namedArgs,true); } finally { c.afterStaticConstructor(pc, parent); } } } else { try { parent=c.beforeStaticConstructor(pc); if(args!=null)rtn=udf.call(pc,calledName,args,true); else rtn=udf.callWithNamedValues(pc,calledName,namedArgs,true); } finally { c.afterStaticConstructor(pc, parent); } } } return rtn; } @Override public boolean isInitalized() { return true; } @Override public void initialize(PageContext pc) { } @Override public void release(PageContext pc) { } @Override public int getType() { return SCOPE_VARIABLES; } @Override public String getTypeAsString() { return "variables"; } @Override public void setBind(boolean bind) {} @Override public boolean isBind() { return true; } @Override public DumpData toDumpData(PageContext pageContext, int maxlevel, DumpProperties dp) { int access=c.getAccess(pageContext); DumpTable table = new DumpTable("component","#99cc99","#ccffcc","#000000"); table.setTitle("Static Scope from Component "+cp.getComponentName()); table.setComment("Only the functions and data members that are accessible from your location are displayed"); DumpTable content = _toDumpData(c.top,pageContext,maxlevel,dp,access); if(!content.isEmpty())table.appendRow(1,new SimpleDumpData(""),content); return table; } DumpTable _toDumpData(ComponentImpl ci,PageContext pc, int maxlevel, DumpProperties dp,int access) { maxlevel--; DumpTable[] accesses=new DumpTable[4]; accesses[Component.ACCESS_PRIVATE] = new DumpTable("#ff6633","#ff9966","#000000"); accesses[Component.ACCESS_PRIVATE].setTitle("private"); accesses[Component.ACCESS_PRIVATE].setWidth("100%"); accesses[Component.ACCESS_PACKAGE] = new DumpTable("#ff9966","#ffcc99","#000000"); accesses[Component.ACCESS_PACKAGE].setTitle("package"); accesses[Component.ACCESS_PACKAGE].setWidth("100%"); accesses[Component.ACCESS_PUBLIC] = new DumpTable("#ffcc99","#ffffcc","#000000"); accesses[Component.ACCESS_PUBLIC].setTitle("public"); accesses[Component.ACCESS_PUBLIC].setWidth("100%"); Iterator<Entry<Key, Member>> it = all(new HashMap<Key,Member>()).entrySet().iterator(); Entry<Key, Member> e; while(it.hasNext()) { e = it.next(); int a=access(pc,e.getValue().getAccess()); DumpTable box=accesses[a]; Object o=e.getValue().getValue(); if(DumpUtil.keyValid(dp,maxlevel, e.getKey())) box.appendRow(1,new SimpleDumpData(e.getKey().getString()),DumpUtil.toDumpData(o,pc,maxlevel,dp)); } DumpTable table=new DumpTable("#ffffff","#cccccc","#000000"); if(!accesses[Component.ACCESS_PUBLIC].isEmpty()) { table.appendRow(0,accesses[Component.ACCESS_PUBLIC]); } if(!accesses[Component.ACCESS_PACKAGE].isEmpty()) { table.appendRow(0,accesses[Component.ACCESS_PACKAGE]); } if(!accesses[Component.ACCESS_PRIVATE].isEmpty()) { table.appendRow(0,accesses[Component.ACCESS_PRIVATE]); } return table; } private int access(PageContext pc, int access) { if(access>-1) return access; return pc.getConfig().getComponentDataMemberDefaultAccess(); } public Component getComponent() { return c; } }