001/*
002 * This file is part of McIDAS-V
003 *
004 * Copyright 2007-2024
005 * Space Science and Engineering Center (SSEC)
006 * University of Wisconsin - Madison
007 * 1225 W. Dayton Street, Madison, WI 53706, USA
008 * https://www.ssec.wisc.edu/mcidas/
009 * 
010 * All Rights Reserved
011 * 
012 * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and
013 * some McIDAS-V source code is based on IDV and VisAD source code.  
014 * 
015 * McIDAS-V is free software; you can redistribute it and/or modify
016 * it under the terms of the GNU Lesser Public License as published by
017 * the Free Software Foundation; either version 3 of the License, or
018 * (at your option) any later version.
019 * 
020 * McIDAS-V is distributed in the hope that it will be useful,
021 * but WITHOUT ANY WARRANTY; without even the implied warranty of
022 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
023 * GNU Lesser Public License for more details.
024 * 
025 * You should have received a copy of the GNU Lesser Public License
026 * along with this program.  If not, see https://www.gnu.org/licenses/.
027 */
028
029package edu.wisc.ssec.mcidasv.util;
030
031import java.util.Map;
032import java.util.Objects;
033import java.util.concurrent.ConcurrentHashMap;
034
035import org.python.core.Py;
036import org.python.core.PyObject;
037import org.python.core.PyString;
038import org.python.core.PyStringMap;
039
040public class LoudPyStringMap extends PyStringMap {
041    
042    private final String mapName;
043    
044    private Map<Object, PyObject> initMap;
045    
046    public LoudPyStringMap(String name) {
047        mapName = name;
048    }
049    
050    public void bake() {
051        if (initMap == null) {
052            initMap = new ConcurrentHashMap<>(this.getMap());
053        }
054    }
055    
056    private boolean isAlwaysAllowed(String key) {
057        // __package__
058        // __doc__
059        // __name__
060        // __builtins__
061        return Objects.equals(key, "__builtins__")
062               || Objects.equals(key, "__doc__")
063               || Objects.equals(key, "__name__")
064               || Objects.equals(key, "__package__");
065    }
066    
067    private boolean isAlwaysAllowed(PyObject key) {
068        String str = key.toString();
069        return isAlwaysAllowed(str);
070    }
071    
072    private boolean checkForDone(Map<Object, PyObject> table) {
073        boolean result = false;
074        if (table.containsKey("___init_finished")) {
075            result = Py.py2boolean(table.get("___init_finished"));
076        }
077        return result;
078    }
079    
080    @Override public PyObject __getitem__(String key) {
081        if (isAlwaysAllowed(key)) {
082            return super.__getitem__(key);
083        }
084        Map<Object, PyObject> table = getMap();
085        if ((initMap == null) && checkForDone(table)) {
086            bake();
087        }
088//        if (initMap != null) {
089//            return initMap.get(key);
090//        } else {
091//            return super.__getitem__(key);
092//        }
093        return super.__getitem__(key);
094    }
095    
096    @Override public PyObject __getitem__(PyObject key) {
097        if (isAlwaysAllowed(key)) {
098            return super.__getitem__(key);
099        }
100        Map<Object, PyObject> table = getMap();
101        if ((initMap == null) && checkForDone(table)) {
102            bake();
103        }
104
105//        if (initMap != null) {
106//            return initMap.get(pyToKey(key));
107//        } else {
108//            return super.__getitem__(key);
109//        }
110        return super.__getitem__(key);
111    }
112    
113    @Override public PyObject __iter__() {
114        Map<Object, PyObject> table = getMap();
115        if ((initMap == null) && checkForDone(table)) {
116            bake();
117        }
118        return super.__iter__();
119    }
120    
121    @Override public void __setitem__(String key, PyObject value) {
122        if (isAlwaysAllowed(key)) {
123            super.__setitem__(key, value);
124            return;
125        }
126        Map<Object, PyObject> table = getMap();
127        if ((initMap == null) && checkForDone(table)) {
128            bake();
129        }
130//        boolean fromInit = false;
131//        if (initMap != null) {
132//            fromInit = initMap.containsKey(key);
133//        }
134//
135//        if (!fromInit) {
136//            super.__setitem__(key, value);
137//        }
138        super.__setitem__(key, value);
139    }
140    
141    @Override public void __setitem__(PyObject key, PyObject value) {
142        if (isAlwaysAllowed(key)) {
143            super.__setitem__(key, value);
144            return;
145        }
146        Map<Object, PyObject> table = getMap();
147        if ((initMap == null) && checkForDone(table)) {
148            bake();
149        }
150//        Object convert = pyToKey(key);
151//        boolean fromInit = false;
152//        if (initMap != null) {
153//            fromInit = initMap.containsKey(convert);
154//        }
155//
156//        if (!fromInit) {
157//            super.__setitem__(key, value);
158//        }
159        super.__setitem__(key, value);
160    }
161    
162    @Override public void __delitem__(String key) {
163        if (isAlwaysAllowed(key)) {
164            super.__delitem__(key);
165            return;
166        }
167        Map<Object, PyObject> table = getMap();
168        if ((initMap == null) && checkForDone(table)) {
169            bake();
170        }
171//        boolean fromInit = false;
172//        if (initMap != null) {
173//            fromInit = initMap.containsKey(key);
174//        }
175//
176//        if (!fromInit) {
177//            super.__delitem__(key);
178//        }
179        super.__delitem__(key);
180    }
181    
182    @Override public void __delitem__(PyObject key) {
183        if (isAlwaysAllowed(key)) {
184            super.__delitem__(key);
185            return;
186        }
187        Map<Object, PyObject> table = getMap();
188        if ((initMap == null) && checkForDone(table)) {
189            bake();
190        }
191//        Object convert = pyToKey(key);
192//        boolean fromInit = false;
193//        if (initMap != null) {
194//            fromInit = initMap.containsKey(convert);
195//        }
196//
197//        if (!fromInit) {
198//            super.__delitem__(key);
199//        }
200        super.__delitem__(key);
201    }
202    
203    private static PyObject keyToPy(Object objKey){
204        if (objKey instanceof String) {
205            return PyString.fromInterned((String)objKey);
206        } else {
207            return (PyObject)objKey;
208        }
209    }
210    
211    private static Object pyToKey(PyObject pyKey) {
212        if (pyKey instanceof PyString) {
213            return ((PyString)pyKey).internedString();
214        } else {
215            return pyKey;
216        }
217    }
218}