All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.archive.spring.KeyedProperties Maven / Gradle / Ivy

Go to download

The Archive Commons Code Libraries project contains general Java utility libraries, as used by the Heritrix crawler and other projects.

There is a newer version: 3.4.0-20220727
Show newest version
/*
 *  This file is part of the Heritrix web crawler (crawler.archive.org).
 *
 *  Licensed to the Internet Archive (IA) by one or more individual 
 *  contributors. 
 *
 *  The IA licenses this file to You under the Apache License, Version 2.0
 *  (the "License"); you may not use this file except in compliance with
 *  the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
 
 package org.archive.spring;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentSkipListMap;
import java.util.logging.Logger;

/**
 * Map for storing overridable properties. 
 * 
 * An object wanting to allow its properties to be overridden 
 * contextually will store those properties in this map. Its 
 * accessors (like getProp() and setProp()) will only pass-through
 * to the 'prop' entry in this map.)
 * 
 */
public class KeyedProperties extends ConcurrentSkipListMap {
    private static final long serialVersionUID = 2L;
    
    private static final Logger logger = Logger.getLogger(KeyedProperties.class.getName());

    /** the alternate global property-paths leading to this map 
     * TODO: consider if deterministic ordered list is important */
    protected TreeSet externalPaths = new TreeSet(); 
    
    /**
     * Add a path by which the outside world can reach this map
     * @param path String path
     */
    public void addExternalPath(String path) {
        externalPaths.add(path);
    }

    /**
     * Get the given value, checking override maps if appropriate.
     * 
     * @param key
     * @return discovered override, or local value
     */
    public Object get(String key) {
        ArrayList overlays = threadOverrides.get();
        for(int i = overlays.size()-1; i>=0; i--) {
            OverlayContext ocontext = overlays.get(i); 
            for(int j = ocontext.getOverlayNames().size()-1; j>=0; j--) {
                String name = ocontext.getOverlayNames().get(j);
                Map m = ocontext.getOverlayMap(name);
                if (m != null) { 
                    for(String ok : getOverrideKeys(key)) {
                        Object val = m.get(ok);
                        if(val!=null) {
                            return val;
                        }
                    }
                } else {
                    logger.warning("sheet '" + name + "' should apply but there is no such sheet!");
                }
            }
        }

        return super.get(key);
    }

    /**
     * Compose the complete keys (externalPath + local key name) to use
     * for checking for contextual overrides. 
     * 
     * @param key local key to compose
     * @return List of full keys to check
     */
    protected List getOverrideKeys(String key) {
        ArrayList keys = new ArrayList(externalPaths.size());
        for(String path : externalPaths) {
            keys.add(path+"."+key);
        }
        return keys;
    }

    
    //
    // CLASS SERVICES
    //
    
    /**
     * ThreadLocal (contextual) collection of pushed override maps
     */
    protected static ThreadLocal> threadOverrides = 
        new ThreadLocal>() {
        protected ArrayList initialValue() {
            return new ArrayList();
        }
    };
    /**
     * Add an override map to the stack 
     */
    static public void pushOverrideContext(OverlayContext ocontext) {
        threadOverrides.get().add(ocontext);
    }
    
    /**
     * Remove last-added override map from the stack
     * @return Map removed
     */
    static public OverlayContext popOverridesContext() {
        // TODO maybe check that pop is as expected
        return threadOverrides.get().remove(threadOverrides.get().size()-1);
    }
    
    static public void clearAllOverrideContexts() {
        threadOverrides.get().clear(); 
    }
    
    static public void loadOverridesFrom(OverlayContext ocontext) {
        assert ocontext.haveOverlayNamesBeenSet();
        pushOverrideContext(ocontext);
    }
    
    static public boolean clearOverridesFrom(OverlayContext ocontext) {
        return threadOverrides.get().remove(ocontext);
    }
    
    static public void withOverridesDo(OverlayContext ocontext, Runnable todo) {
        try {
            loadOverridesFrom(ocontext);
            todo.run();
        } finally {
            clearOverridesFrom(ocontext); 
        }
    }

    public static boolean overridesActiveFrom(OverlayContext ocontext) {
        return threadOverrides.get().contains(ocontext);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy