001/**
002 *
003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved.
004 *
005 * This library is free software; you can redistribute it and/or
006 * modify it under the terms of the GNU Lesser General Public
007 * License as published by the Free Software Foundation; either 
008 * version 2.1 of the License, or (at your option) any later version.
009 * 
010 * This library is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013 * Lesser General Public License for more details.
014 * 
015 * You should have received a copy of the GNU Lesser General Public 
016 * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
017 * 
018 **/
019/**
020 * Implements the CFML Function structfindkey
021 */
022package lucee.runtime.functions.struct;
023
024import java.util.Iterator;
025import java.util.List;
026import java.util.Map;
027import java.util.Map.Entry;
028
029import lucee.runtime.PageContext;
030import lucee.runtime.exp.FunctionException;
031import lucee.runtime.exp.PageException;
032import lucee.runtime.functions.BIF;
033import lucee.runtime.op.Caster;
034import lucee.runtime.type.Array;
035import lucee.runtime.type.ArrayImpl;
036import lucee.runtime.type.Collection;
037import lucee.runtime.type.Collection.Key;
038import lucee.runtime.type.Struct;
039import lucee.runtime.type.StructImpl;
040import lucee.runtime.type.scope.Argument;
041import lucee.runtime.type.util.KeyConstants;
042import lucee.runtime.type.wrap.ListAsArray;
043import lucee.runtime.type.wrap.MapAsStruct;
044
045public final class StructFindKey extends BIF {
046        
047        private static final long serialVersionUID = 598706098288773975L;
048        
049        public static Array call(PageContext pc , lucee.runtime.type.Struct struct, String value) throws PageException {
050                return _call(pc,struct,value,false);
051        }
052    public static Array call(PageContext pc , Struct struct, String value, String scope) throws PageException {
053        // Scope
054        boolean all=false;
055        if(scope.equalsIgnoreCase("one")) all=false;
056        else if(scope.equalsIgnoreCase("all")) all=true;
057        else throw new FunctionException(pc,"structFindValue",3,"scope","invalid scope definition ["+scope+"], valid scopes are [one, all]");
058        return _call(pc,struct,value,all);
059    }
060    private static Array _call(PageContext pc , Struct struct, String value, boolean all) throws PageException {
061        Array array=new ArrayImpl();
062        getValues(array,struct,value,all,"");
063        return array;
064    }
065    /**
066     * @param coll
067     * @param value
068     * @param all
069     * @param buffer
070     * @return
071     * @throws PageException
072     */
073    private static boolean getValues(Array array,Collection coll, String value, boolean all, String path) throws PageException {
074        //Collection.Key[] keys=coll.keys();
075        Iterator<Entry<Key, Object>> it = coll.entryIterator();
076        Entry<Key, Object> e;
077        boolean abort=false;
078        Collection.Key key;
079        
080        while(it.hasNext()) {
081                e = it.next();
082            if(abort)break;
083            key=e.getKey();
084            Object o=e.getValue();
085
086            // matching value  (this function search first for base)
087            if(key.getString().equalsIgnoreCase(value)) {
088                Struct sct=new StructImpl();
089                
090                    sct.setEL(KeyConstants._value,o);
091                sct.setEL(KeyConstants._path,createKey(coll,path,key));
092                sct.setEL(KeyConstants._owner,coll);
093                array.append(sct);
094                if(!all)abort=true;
095            }
096            
097            // Collection
098            if(!abort) {
099                    if(o instanceof Collection) {
100                        abort=getValues(array,((Collection)o), value, all, createKey(coll,path,key));
101                    }
102                    else if(o instanceof List){
103                        abort=getValues(array,ListAsArray.toArray((List<?>)o), value, all, createKey(coll,path,key));
104                    }
105                    else if(o instanceof Map){
106                        abort=getValues(array,MapAsStruct.toStruct((Map<?,?>)o), value, all, createKey(coll,path,key));
107                    }
108            }
109        }
110        
111        return abort;
112    }
113        static String createKey(Collection coll,String path,Collection.Key key) {
114                StringBuilder p=new StringBuilder(path.toString());
115        if(isArray(coll)){
116                p.append('[').append(key.getString()).append(']');
117        }
118        else{
119                p.append('.').append(key.getString());
120        }
121                return p.toString();
122        }
123        static boolean isArray(Collection coll) {
124                return coll instanceof Array && !(coll instanceof Argument);
125        }
126        @Override
127        public Object invoke(PageContext pc, Object[] args) throws PageException {
128                if(args.length==3) return call(pc,Caster.toStruct(args[0]),Caster.toString(args[1]),Caster.toString(args[2]));
129                return call(pc,Caster.toStruct(args[0]),Caster.toString(args[1]));
130        }
131}