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

com.thegoate.Goate.orig Maven / Gradle / Ivy

/*
 * Copyright (c) 2017. Eric Angeli
 *
 *  Permission is hereby granted, free of charge,
 *  to any person obtaining a copy of this software
 *  and associated documentation files (the "Software"),
 *  to deal in the Software without restriction,
 *  including without limitation the rights to use, copy,
 *  modify, merge, publish, distribute, sublicense,
 *  and/or sell copies of the Software, and to permit
 *  persons to whom the Software is furnished to do so,
 *  subject to the following conditions:
 *
 *  The above copyright notice and this permission
 *  notice shall be included in all copies or substantial
 *  portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 *  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
 *  WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
 *  AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 *  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 *  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 *  DEALINGS IN THE SOFTWARE.
 */

package com.thegoate;

import com.thegoate.annotations.AnnotationFactory;
import com.thegoate.annotations.GhostProtocol;
import com.thegoate.dsl.Interpreter;
import com.thegoate.logging.volume.Diary;
import com.thegoate.reflection.GoateReflection;
import com.thegoate.utils.compare.Compare;
import com.thegoate.utils.fill.serialize.*;
import com.thegoate.utils.togoate.ToGoate;

import java.lang.reflect.InvocationTargetException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

import static com.thegoate.expect.validate.Validate.HEALTH_CHECK;
import static com.thegoate.logging.volume.VolumeKnob.volume;
import static java.lang.Thread.currentThread;

/**
 * The manager for the collection of test data.
 * Created by gtque on 4/19/2017.
 */
public class Goate implements HealthMonitor, Diary, Cloneable {

    private volatile Map data = new ConcurrentHashMap<>();
    @GoateIgnore
    public static final String GOATE_VARIABLE_PREFIX = "_goate_(%$#)_";
    @GoateIgnore
    private volatile Map ghosted = new ConcurrentHashMap<>();
    @GoateIgnore
    public volatile static List ghosts = null;
    @GoateIgnore
    private Interpreter dictionary;
    @GoateIgnore
    boolean increment = true;
    @GoateIgnore
    static volatile Map prettyPrintTabs = new ConcurrentHashMap<>();

    public Goate() {
        init();
    }

    public Goate(Map initialData) {
        init();
        putMap(initialData);
    }

    protected void init() {
        dictionary = new Interpreter(this);
        if (ghosts == null) {
            ghosts = Collections.synchronizedList(new ArrayList<>());
            findGhosts();
        }
    }

    private void findGhosts() {
        AnnotationFactory af = new AnnotationFactory().annotatedWith(GhostProtocol.class).buildDirectory();
        Map directory = AnnotationFactory.directory.get(GhostProtocol.class.getCanonicalName());
        for (Map.Entry entry : directory.entrySet()) {
            //ghosts.add(""+entry.getValue());
            Class thGhost = (Class) entry.getValue();
            GhostProtocol gp = (GhostProtocol) thGhost.getAnnotation(GhostProtocol.class);
            ghosts.addAll(Arrays.asList(gp.ghosts()));
        }
    }

    public Goate autoIncrement(boolean increment) {
        this.increment = increment;
        return this;
    }

    public int size() {
        int size = 0;
        if (data != null) {
            size = data.size();
        }
        return size;
    }

    public Map data() {
        Map mapped = new ConcurrentHashMap<>();
        for (String key : keys()) {
            mapped.put(key, get(key));
        }
        stale = true;
        return mapped;
    }

    public boolean containsKey(String key) {
        return data.containsKey(key);
    }

    public Set keys() {
        return new TreeSet<>(data.keySet());
    }

    public List keysSorted() {
        List list = new ArrayList<>();
        Set keys = keys();
        if (keys != null) {
            list.addAll(keys);
            Collections.sort(list);
        }
        return list;
    }

    public String[] keysArray() {
        String[] keys = new String[data.keySet().size()];
        int i = 0;
        for (String key : data.keySet()) {
            keys[i] = key;
            i++;
        }
        return keys;
    }

    public Goate putMap(Map map) {
        if (map != null) {
            map.entrySet().parallelStream().forEach(e -> put("" + e.getKey(), e.getValue()));
        }
        return this;
    }

    public Goate put(String key, Object value) {
        return put(key, value, true);
    }

    public Goate put(String key, Object value, boolean includeNull) {
        if (data == null) {
            data = new ConcurrentHashMap<>();
        }
        if (ghosts != null) {
            if (ghosts.contains(key)) {
                ghosted.put(key, value == null ? "null::" : value);
                return this;
            }
        }
        //        if(value!=null) {
        if (value != null || includeNull) {
            if (key.contains("##")) {
                key = buildKey(key);
            }
            data.put(key, value == null ? "null::" : value);//can't put null in a map, so wrap in the null dsl.
            //        }
            stale = true;
        }
        return this;
    }

    public boolean filterOnKey(String key) {
        return key.contains("##");
    }

    public String buildKey(String key) {
        String fullKey = key;
        if (increment) {
            Set keys = data.keySet();
            while (fullKey.contains("##")) {
                String partialKey = fullKey.substring(0, fullKey.indexOf("##"));
                long size = keys.parallelStream().filter(id -> id.matches(partialKey + "[0-9]+.*")).count();
                fullKey = fullKey.replaceFirst("##", "" + size);
            }
        } else {
            fullKey = key.replace("##", "" + System.nanoTime());
        }
        return fullKey;
    }

    public Object getStrict(String key) {
        return get(key, null, false);
    }

    public Object getStrict(String key, Object def) {
        return get(key, def, false);
    }

    public Object get(int index) {
        Iterator keys = data.keySet().iterator();
        String key = "";
        while (index >= 0) {
            key = keys.next();
            index--;
        }
        return data.get(key);
    }

    public Object get(String key) {
        return get(key, null, true);
    }

    public Object get(String key, Object def) {
        return get(key, def, true);
    }

    public Object get(String key, Object def, boolean dsl) {
        return get(key, def, dsl, Object.class);
    }

    public  T get(String key, Object def, Class type) {
        return get(key, def, true, type);
    }

    public  T get(String key, Object def, boolean dsl, Class type) {
        Object value = null;
        if (key != null && !key.isEmpty() && !key.equalsIgnoreCase("username")) {
            value = System.getProperty(key);
        }
        if (filterOnKey(key)) {
            value = filter(key.replace("##", "[0-9]*"));
        } else {
            if (value == null) {
                if (key.equals("username")) {//username is a special key name, in most cases we want to use the one set in the collection
                    //so it is checked first, and then if it is null it, check using the normal flow.
                    value = data.get(key);
                }
                if (value == null) {
                    if (key != null && !key.isEmpty() && !key.equalsIgnoreCase("username")) {
                        value = System.getenv(key);
                    }
                    if (value == null) {
                        if (ghosts.contains(key)) {
                            if (ghosted.containsKey(key)) {
                                value = ghosted.get(key);
                            } else if (def != null) {
                                ghosted.put(key, def);
                                value = def;
                            }
                        } else {
                            if (data.containsKey(key)) {
                                value = data.get(key);
                            } else if (def != null) {
                                data.put(key, def);
                                stale = true;
                                value = def;
                            }
                        }
                    }
                }
            }
            if (value == null && !data.containsKey(key) && !ghosted.containsKey(key)) {
                value = def;
            }
            if (value != null && dsl) {
                value = processDSL(key, value);
            }
        }
        return doCast(value, type);
    }

    private  T doCast(Object value, Class type) {
        try {
            return new GoateReflection().isPrimitive(type) ? doCastPrimitive(value, type) : type.cast(value);
        } catch (ClassCastException cce) {
            try {
                return new Cast(this, DefaultSource.class).cast(value, type);
            } catch (IllegalAccessException | InstantiationException e) {
                //swallow the exception and just try a type.cast again and let that throw an exception.
            }
        }
        return type.cast(null);
    }

    public  T doCastPrimitive(Object value, Class type) {
        GoateReflection gr = new GoateReflection();
        if (gr.isBooleanType(type) && gr.isBoolean(value)) {
            value = Boolean.parseBoolean("" + value);
        } else if (gr.isByteType(type) && gr.isByte(value)) {
            value = Byte.parseByte("" + value);
        } else if (gr.isIntegerType(type) && gr.isInteger(value)) {
            value = Integer.parseInt("" + value);
        } else if (gr.isDoubleType(type) && gr.isDouble(value)) {
            value = Double.parseDouble("" + value);
        } else if (gr.isFloatType(type) && gr.isFloat(value)) {
            value = Float.parseFloat("" + value);
        } else if (gr.isLongType(type) && gr.isLong(value)) {
            value = Long.parseLong("" + value);
        } else if (gr.isCharacterType(type) && gr.isCharacter(value)) {
            value = Character.valueOf(("" + value).charAt(0));
        } else if (gr.isShortType(type) && gr.isShort(value)) {
            value = Short.parseShort("" + value);
        }
        return type.cast(value);
    }

    public Object processDSL(Object value) {
        return processDSL("", value);
    }

    public Object processDSL(String key, Object value) {
        if (value != null) {
            String check = "" + value;
            if (check.contains("::")) {
                check = check.substring(0, check.indexOf("::"));
            } else {
                check = null;
            }
            if (check != null) {
                value = dictionary.translate(key, check, value);
            }
        }
        return value;
    }

    public Goate drop(String key) {
        if (ghosts.contains(key)) {
            ghosted.remove(key);
        } else {
            data.remove(key);
            stale = true;
        }
        return this;
    }

    public Goate scrub(String pattern) {
        Goate scrub = filter(pattern);
        if (scrub != null) {
            for (String key : scrub.keys()) {
                drop(key);
            }
        }
        stale = true;
        return this;
    }

    public String findKeyIgnoreCase(String find) {
        String key = find;
        Optional found = keys().parallelStream().filter(k -> k.equalsIgnoreCase(find)).findFirst();
        if (found.isPresent()) {
            key = found.get();
        }
        return key;
    }

    /**
     * Simple filter, matches if key starts with the given pattern.
     *
     * @param pattern The pattern to match
     * @return A Goate collection containing matching elements.
     */
    public Goate filter(String pattern) {
        return filterStrict(pattern + ".*");
    }

    /**
     * Simple filter, matches if key matches the given pattern
     *
     * @param pattern The pattern to match
     * @return A Goate collection containing matching elements.
     */
    public Goate filterStrict(String pattern) {
        Goate filtered = new Goate();
        if (data != null) {
            keys().parallelStream().forEach(key -> addToFiltered(filtered, key, key.matches(pattern)));
        }
        return filtered;
    }

    protected void addToFiltered(Goate filtered, String key, boolean add) {
        if (add) {
            filtered.put(key, getStrict(key));
        }
    }

    /**
     * Simple filter, matches if key does start with the given pattern; ie excludes anything matching the pattern
     *
     * @param pattern The pattern to match
     * @return A Goate collection containing matching elements.
     */
    public Goate filterExclude(String pattern) {
        Goate filtered = new Goate();
        if (data != null) {
            String fullPattern = pattern + ".*";
            keys().parallelStream().forEach(key -> addToFiltered(filtered, key, !key.matches(fullPattern)));
        }
        return filtered;
    }

    public Goate filterByValue(Object value) {
        Goate filtered = new Goate();
        if (data != null) {
            for (String key : keys()) {
                if (new Compare(get(key)).using("==").to(value).evaluate()) {
                    filtered.put(key, get(key));
                }
            }
        }
        return filtered;
    }

    public Goate filterExcludeByValue(Object value) {
        Goate filtered = new Goate();
        if (data != null) {
            for (String key : keys()) {
                if (new Compare(get(key)).using("!=").to(value).evaluate()) {
                    filtered.put(key, get(key));
                }
            }
        }
        return filtered;
    }

    public Goate scrubKeys(String pattern) {
        return scrubKeys(pattern, "");
    }

    public Goate scrubKeys(String pattern, String sponge) {
        return scrubSubKeys(".*", pattern, sponge);
    }

    public Goate scrubKeys(String pattern, Sponge sponge) {
        return scrubSubKeys(".*", pattern, sponge);
    }

    public Goate scrubSubKeys(String pattern, String subkey, String sponge) {
        //        Goate scrubbed = new Goate();
        return scrubSubKeys(pattern, subkey, new Sponge() {
            @Override
            public String soap(String dirty) {

                return sponge;
            }
        });
    }

    public Goate scrubSubKeys(String pattern, String subkey, Sponge sponge) {
        List scrubbed = new ArrayList<>();
        List not_scrubbed = new ArrayList<>();
        if (data != null) {
            for (Map.Entry entry : data.entrySet()) {
                if (entry.getKey().matches(pattern)) {
                    String soap;
                    if (subkey == null) {
                        soap = entry.getKey().replaceFirst(pattern, sponge.soap(entry.getKey()));
                    } else {
                        soap = entry.getKey().replaceFirst(subkey, sponge.soap(entry.getKey()));
                    }
                    if (soap != null && !soap.equals(entry.getKey())) {
                        scrubbed.add(entry.getKey());
                        this.put(soap, entry.getValue());
                    }
                    not_scrubbed.add(soap);
                }
            }
            scrubbed.parallelStream()
                    .filter(scrub -> !not_scrubbed.contains(scrub))
                    .collect(Collectors.toList())
                    .parallelStream().forEach(scrub -> drop(scrub));
        }
        stale = true;
        return this;
    }

    public Goate filterAndSplitKeyValuePairs(String filter) {
        return filterAndSplitKeyValuePairs(filter, ":=");
    }

    public Goate filterAndSplitKeyValuePairs(String filter, String split) {
        Goate filtered = new Goate();
        if (data != null) {
            Goate info = filter(filter);
            for (String key : info.keys()) {
                Object strictDef = info.getStrict(key);
                String def = "" + (strictDef == null ? "" : strictDef);
                if (!def.contains(split)) {
                    def = "" + processDSL(def);
                }
                if (def.contains(split)) {
                    String k = def.substring(0, def.indexOf(split));
                    String v = def.substring(def.indexOf(split) + split.length());
                    filtered.put("" + processDSL(k), processDSL(v));
                }
            }
        }
        return filtered;
    }

    public Goate merge(Goate merge, boolean replace) {
        if (merge != null) {
            Set myKeys = keys();
            for (String key : merge.keys()) {
                if (replace) {
                    put(key, merge.getStrict(key));
                } else {
                    if (!myKeys.contains(key)) {
                        put(key, merge.getStrict(key));
                    }
                }
            }
        }
        stale = true;
        return this;
    }

    private String tabs(int numberOfTabs) {
        StringBuilder tabs = new StringBuilder();
        for (; numberOfTabs > 0; numberOfTabs--) {
            tabs.append("\t");
        }
        return tabs.toString();
    }

    public String toString() {
        String prepadding = "";
//		if(prettyPrintTabs.containsKey(currentThread().getName())){
//			prepadding += prettyPrintTabs.get(currentThread().getName());
//		}
        return toString(prepadding, "");
    }

    //public static volatile int innerGoate = 0;

    public String toString(String prepadding, String postpadding) {
//		if(prettyPrintTabs.containsKey(currentThread().getName())){
//			prepadding += prettyPrintTabs.get(currentThread().getName());
//		}
//		prettyPrintTabs.put(currentThread().getName(),prepadding);
        return toString(prepadding, postpadding, true);
    }

    public String toString(String prepadding, String postpadding, boolean newLine) {
        if (stale) {
            int tabCount = 0;
            if (prettyPrintTabs.containsKey(currentThread().getName())) {
                tabCount = prettyPrintTabs.get(currentThread().getName());
//				prepadding += initial_padding;
            }
            String tabs = tabs(tabCount);
            String tabInnerGoate = tabs(tabCount);
            StringBuilder sb = new StringBuilder();
            if (tabCount > 0) {
//				sb.append("\n").append(prepadding).append(tabInnerGoate).append("Goate[");
                tabs = tabs(tabCount + 1);
                sb.append("Goate[");
                if (newLine) {
                    sb.append("\n");
//					tabCount++;
//					tabs = tabs(tabCount);
                }
            }
            // parentInner = innerGoate;
            boolean appendNewLine = false;
            prettyPrintTabs.put(currentThread().getName(), tabCount + 1);
            for (String key : keys()) {
                if (appendNewLine && newLine) {
                    sb.append("\n");
                } else if (appendNewLine && !newLine) {
                    sb.append("; ");
                }
                appendNewLine = true;
                Object message = data.get(key);
                if (key.startsWith(HEALTH_CHECK)) {
                    message = new Veterinarian((Goate) message);
                    key = key.replace(HEALTH_CHECK, "");
//					prettyPrintTabs.put(currentThread().getName(), tabCount+2);
                }
                sb.append(prepadding).append(tabs).append(key).append(":").append(volume(message)).append(postpadding);
            }
            prettyPrintTabs.put(currentThread().getName(), tabCount);
            if (tabCount > 0) {
                if (newLine) {
                    sb.append("\n");
                }
                sb.append(tabInnerGoate).append("]");
            }
            entry = sb.toString();
//			prettyPrintTabs.put(currentThread().getName(),initial_padding);
        }
        return entry;
    }

    @Override
    public boolean equals(Object check) {
        return compare(check) == 0;
    }

    private Object health = null;
    private Object keySet = null;

    private Goate keySet() {
        return (Goate) keySet;
    }

    public Goate healthCheck() {
        return (Goate) health;
    }

    public String pad(String text, String pad) {
        StringBuilder sb = new StringBuilder();
        sb.append("%_goate_start%");
        Arrays.stream(text.split("\n")).forEach(line -> sb.append(pad).append(line).append("\n"));
        return sb.toString().trim().replace("%_goate_start%", "");
    }

    public int compare(Object check) {
        boolean resetSet = true;
        //        if(keySet == null){
        health = new Goate();
        keySet = new Goate();
        //            resetSet = true;
        //        }

        int result = size();
        if (!(check instanceof Goate)) {
            Goate castCheck = new ToGoate(check).convert();
            if (castCheck != null && !(castCheck.size() == 1 && castCheck.keys().contains("_original_"))) {
                check = castCheck;
            }
        }
        if (check instanceof Goate) {
            Goate gCheck = (Goate) check;
            result = 0;
            for (String key : keys()) {
                boolean found = false;
                Object o = get(key);
                String keyPattern = key.replaceAll("[0-9]+", "[0-9]+").replace(".", "\\.");
                Set keySet = keySet().get(keyPattern, null, Set.class);
                if (keySet == null) {
                    keySet = gCheck.filterStrict(keyPattern).keys();
                    keySet().put(keyPattern, keySet);
                }
                for (String checkKey : keySet) {
                    if (new Compare(o).to(gCheck.get(checkKey)).using("==").evaluate()) {
                        found = true;
                        break;
                    } else {
                        healthCheck().put("not equal##", checkKey + ": " + o + "!=" + gCheck.get(checkKey));
                    }
                }
                if (!found) {
                    result++;
                    healthCheck().put("not found##", key + ": " + o);
                }
            }
        }
        if (resetSet) {
            keySet = null;
        }

        return result;
    }

<<<<<<< HEAD
    private volatile String entry = "";
    private volatile boolean stale;
=======
	@GoateIgnore
	private volatile String entry = "";
	@GoateIgnore
	private volatile boolean stale;
>>>>>>> f7af82b (implement deeper clone)

    @Override
    public String mostRecentEntry() {
        if (stale) {
            writeEntry(volume(this, false));
        }
        return entry;
    }

    @Override
    public void writeEntry(String entry) {
        stale = false;
    }

<<<<<<< HEAD
    public Object clone() {
        Goate clone = new Goate();
        GoateReflection gr = new GoateReflection();
        for (String key : keys()) {
            Object value = getStrict(key);
            if (gr.isPrimitive(gr.primitiveType(value))) {
                clone.put(key, value);
            } else if (value == null) {
                clone.put(key, "null::");
            } else {
                if (value instanceof Nanny) {
                    clone.put(key, new DeSerializer().data(new Serializer<>(value).toGoate()).build(value.getClass()));
                } else {
                    //if you find yourself here because your cloned data was not as deep as you had hoped
                    //I am sorry, but I only deep copy pojos that extend kid/nanny at the moment.
                    clone.put(key, value);
                }
            }
        }
        return clone;
    }
=======
	private void copy(Goate clone, String key, Object value, GoateReflection gr) throws CloneNotSupportedException{
		if (gr.isPrimitive(gr.primitiveType(value))) {
			clone.put(key, value);
		} else if (value == null) {
			clone.put(key, "null::");
		} else {
			if (value instanceof Nanny) {
				clone.put(key, ((Nanny) value).clone());//new DeSerializer().data(new Serializer<>(value).toGoate()).build(value.getClass()));
			} else {
				//if you find yourself here because your cloned data was not as deep as you had hoped
				//I am sorry, but I only deep copy pojos/objects that extend kid/nanny at the moment.
				//but I will go ahead and try it and see if it works.
				try {
					clone.put(key, gr.cloneValue(value));
				} catch (InvocationTargetException | IllegalAccessException e) {
					//failed to do a deeper clone, so settling for a shallow copy.
					clone.put(key, value);
				}
			}
		}
	}

	public Object clone() throws CloneNotSupportedException {
		Goate clone = new Goate();
		GoateReflection gr = new GoateReflection();
		StringBuilder cloned = new StringBuilder();
		data.entrySet().parallelStream().forEach(entry -> {
			try {
				copy(clone, entry.getKey(), entry.getValue(), gr);
			} catch (CloneNotSupportedException e) {
				cloned.append(e.getMessage()).append("\n");
			}
		});
		if(cloned.length()>0) {
			throw new CloneNotSupportedException(cloned.toString());
		}
		return clone;
	}
>>>>>>> f7af82b (implement deeper clone)
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy