package org.intellimate.izou.sdk.frameworks.music.player;

import org.intellimate.izou.resource.ResourceModel;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Optional;
import java.util.function.BiPredicate;

/**
 * This class represents the Information about one track, this class is immutable
 * @author LeanderK
 * @version 1.0
 */
@SuppressWarnings("unused")
public class TrackInfo {
    public static final String nameDescriptor = "izou.music.trackinfo.name";
    private final String name;
    public static final String artistDescriptor = "izou.music.trackinfo.artist";
    private final String artist;
    public static final String albumDescriptor = "izou.music.trackinfo.album";
    private final String album;
    public static final String albumCoverDescriptor = "izou.music.trackinfo.albumCover";
    private final byte[] albumCover;
    public static final String albumCoverFormatDescriptor = "izou.music.trackinfo.albumCoverFormat";
    private final String albumCoverFormat;
    public static final String dataDescriptor = "izou.music.trackinfo.data";
    private final String data;
    public static final String yearDescriptor = "izou.music.trackinfo.year";
    private final String year;
    public static final String genreDescriptor = "izou.music.trackinfo.genre";
    private final String genre;
    public static final String bmpDescriptor = "izou.music.trackinfo.bmp";
    private final String bmp;
    public static final String durationDescriptor = "izou.music.trackinfo.duration";
    private final long duration;

    public TrackInfo(String name) {
        this.name = name;
        this.artist = null;
        this.album = null;
        albumCover = null;
        albumCoverFormat = null;
        data = null;
        year = null;
        genre = null;
        bmp = null;
        duration = -1;
    }

    public TrackInfo(String name, String artist, String album) {
        this.name = name;
        this.artist = artist;
        this.album = album;
        albumCover = null;
        albumCoverFormat = null;
        data = null;
        year = null;
        genre = null;
        bmp = null;
        duration = -1;
    }

    public TrackInfo(String name, String artist, String album, byte[] albumCover, String albumCoverFormat) {
        this.name = name;
        this.artist = artist;
        this.album = album;
        this.albumCover = albumCover;
        this.albumCoverFormat = albumCoverFormat;
        data = null;
        year = null;
        genre = null;
        bmp = null;
        duration = -1;
    }

    public TrackInfo(String name, String artist, String album, byte[] albumCover, String albumCoverFormat, String data) {
        this.name = name;
        this.artist = artist;
        this.album = album;
        this.albumCover = albumCover;
        this.albumCoverFormat = albumCoverFormat;
        this.data = data;
        year = null;
        genre = null;
        bmp = null;
        duration = -1;
    }

    public TrackInfo(String name, String artist, String album, byte[] albumCover, String albumCoverFormat, String data, String year, String genre, String bmp, long duration) {
        this.name = name;
        this.artist = artist;
        this.album = album;
        this.albumCover = albumCover;
        this.albumCoverFormat = albumCoverFormat;
        this.data = data;
        this.year = year;
        this.genre = genre;
        this.bmp = bmp;
        this.duration = duration;
    }

    /**
     * returns a String containing the Name of the Track
     * @return the optional Name
     */
    public Optional<String> getName() {
        return getOptionalOrEmtpyIfNull(name);
    }

    /**
     * returns a String containing the Name of the Artist
     * @return the optional Artist
     */
    public Optional<String> getArtist() {
        return getOptionalOrEmtpyIfNull(artist);
    }

    /**
     * returns a String containing the Name of the Album
     * @return the optional Artist
     */
    public Optional<String> getAlbum() {
        return getOptionalOrEmtpyIfNull(album);
    }

    /**
     * returns the album cover as bytes, format specified by: {@link #getAlbumCoverFormat()}.
     * @return the optional Album Cover
     */
    public Optional<byte[]> getAlbumCover() {
        return getOptionalOrEmtpyIfNull(albumCover);
    }

    /**
     * returns the format of the album cover, which can be obtained by calling {@link #getAlbumCover()}.
     * @return the optional Format
     */
    public Optional<String> getAlbumCoverFormat() {
        return getOptionalOrEmtpyIfNull(albumCoverFormat);
    }

    /**
     * returns the data of the TrackInfo.
     * <p>
     * this field is mostly internal, can be an url etc. should be used only if obtained from a player
     * </p>
     * @return the optional data
     */
    public Optional<String> getData() {
        return getOptionalOrEmtpyIfNull(data);
    }

    /**
     * returns a String containing the Year of the release
     * @return the optional Year of the release
     */
    public Optional<String> getYear() {
        return getOptionalOrEmtpyIfNull(year);
    }

    /**
     * returns a String containing the Genre
     * @return the optional Genre
     */
    public Optional<String> getGenre() {
        return getOptionalOrEmtpyIfNull(genre);
    }

    /**
     * returns a String containing the BMP
     * @return the optional BMP
     */
    public Optional<String> getBmp() {
        return getOptionalOrEmtpyIfNull(bmp);
    }

    /**
     * returns a long containing the Duration in milliseconds
     * @return the optional Duration
     */
    public Optional<Long> getDuration() {
        if (duration < 0) {
            return Optional.empty();
        } else {
            return Optional.of(duration);
        }
    }

    private <T> Optional<T> getOptionalOrEmtpyIfNull(T t) {
        if (t == null) {
            return Optional.empty();
        } else {
            return Optional.of(t);
        }
    }

    /**
     * retruns true if any of these arguments is already existing and would be overwritten, otherwise retruns false.
     * The method also returns true if everything is null except the albumCover-Format (maybe known in advance).
     * @param trackInfo the trackInfo to compare against
     * @return true if new
     */
    public boolean isNew(TrackInfo trackInfo) {
        return isNew(trackInfo.name,
                trackInfo.artist,
                trackInfo.album,
                trackInfo.albumCover,
                trackInfo.albumCoverFormat,
                trackInfo.data,
                trackInfo.year,
                trackInfo.genre,
                trackInfo.bmp,
                trackInfo.duration);
    }

    /**
     * retruns true if any of these arguments is already existing and would be overwritten, otherwise retruns false.
     * The method also returns true if everything is null except the albumCover-Format (maybe known in advance).
     * @param name the name
     * @param artist the artist
     * @param album the album
     * @param albumCover the album cover
     * @param albumCoverFormat the album cover format
     * @param id the id of the track
     * @param year the year
     * @param genre the genre
     * @param duration the duration
     * @param bmp the bmp
     * @return true if new
     */
    public boolean isNew(String name, String artist, String album, byte[] albumCover, String albumCoverFormat, String id, String year, String genre, String bmp, long duration) {
        if (name == null && artist == null && album == null && albumCover == null && id == null)
            return true;
        BiPredicate<String, String> compareStrings = (newString, oldString) ->
                oldString != null && newString != null && !oldString.equals(newString);
        if (compareStrings.test(name, this.name)) {
            return true;
        }
        if (compareStrings.test(artist, this.artist)) {
            return true;
        }
        if (compareStrings.test(album, this.album)) {
            return true;
        }
        if (compareStrings.test(id, this.data)) {
            return true;
        }
        if (compareStrings.test(albumCoverFormat, this.albumCoverFormat)) {
            return true;
        }
        if (compareStrings.test(year, this.year)) {
            return true;
        }
        if (compareStrings.test(genre, this.genre)) {
            return true;
        }
        if (this.duration > 0 && duration > 0 && this.duration == duration) {
            return true;
        }
        if (compareStrings.test(bmp, this.bmp)) {
            return true;
        }
        if (albumCover != null) {
            if (this.albumCover == null || !Arrays.equals(albumCover, this.albumCover))
                return true;
        }
        return false;
    }

    /**
     * returns a trackinfo if some information was added and not overwritten
     * @param trackInfo the data to compare
     * @return a trackinfo if some information was updated
     */
    public Optional<TrackInfo> update(TrackInfo trackInfo) {
        return update(trackInfo.name,
                trackInfo.artist,
                trackInfo.album,
                trackInfo.albumCover,
                trackInfo.albumCoverFormat,
                trackInfo.data,
                trackInfo.year,
                trackInfo.genre,
                trackInfo.bmp,
                trackInfo.duration);
    }

    /**
     * returns a trackinfo if some information was added and not overwritten (see isNew) AND a change occurred.
     * @param name the name
     * @param artist the artist
     * @param album the album
     * @param albumCover the album cover
     * @param coverFormat the album cover format
     * @param data the data
     * @param year the year
     * @param genre the genre
     * @param duration the duration
     * @param bmp the bmp
     * @return a trackInfo if some information was updated, and an empty optional if: 1. information would be ovewritten
     * or 2. no change occured
     */
    public Optional<TrackInfo> update(String name, String artist, String album, byte[] albumCover, String coverFormat, String data, String year, String genre, String bmp, long duration) {
        if (isNew(name, artist, album, albumCover, albumCoverFormat, data, year, genre, bmp, duration))
            return Optional.empty();
        boolean change = false;
        if (name != null && !name.equals(this.name)) {
            change = true;
        }
        if (artist != null && !artist.equals(this.artist)) {
            change = true;
        }
        if (album != null && !album.equals(this.album)) {
            change = true;
        }
        if (name != null && !name.equals(this.name)) {
            change = true;
        }
        if (albumCoverFormat != null && !albumCoverFormat.equals(this.albumCoverFormat)) {
            change = true;
        }
        if (data != null && !data.equals(this.data)) {
            change = true;
        }
        if (year != null && !year.equals(this.year)) {
            change = true;
        }
        if (genre != null && !genre.equals(this.genre)) {
            change = true;
        }
        if (duration > 0 && duration != this.duration) {
            change = true;
        }
        if (bmp != null && !bmp.equals(this.bmp)) {
            change = true;
        }
        if (!change)
            return Optional.empty();
        return Optional.of(new TrackInfo(
                this.name == null? name : this.name,
                this.artist == null? artist : this.artist,
                this.album == null? album : this.album,
                this.albumCover == null? albumCover : this.albumCover,
                this.albumCoverFormat == null? albumCoverFormat : this.albumCoverFormat,
                this.data == null? data : this.data,
                this.year == null? year : this.year,
                this.genre == null? genre : this.genre,
                this.bmp == null? bmp : this.bmp,
                this.duration < 0 ? duration : this.duration
        ));
    }

    /**
     * exports the TrackInfo to a Hashmap
     * @return a HashMap
     */
    public HashMap<String, Object> export() {
        HashMap<String, Object> data = new HashMap<>();
        data.put(nameDescriptor, name);
        data.put(artistDescriptor, artist);
        data.put(albumDescriptor, albumDescriptor);
        data.put(albumCoverDescriptor, albumCover);
        data.put(albumCoverFormatDescriptor, albumCoverFormatDescriptor);
        data.put(dataDescriptor, this.data);
        data.put(yearDescriptor, this.year);
        data.put(genreDescriptor, this.genre);
        data.put(durationDescriptor, this.duration);
        data.put(bmpDescriptor, this.bmp);
        return data;
    }

    /**
     * returns the optional TrackInfo if the HashMap contains no malformed data
     * @param hashMap the data to import from
     * @return an optional
     */
    public static Optional<TrackInfo> importFromHashMap(HashMap<String, Object> hashMap) {
        try {
            String name = (String) hashMap.get(nameDescriptor);
            String album = (String) hashMap.get(albumDescriptor);
            String artist = (String) hashMap.get(artistDescriptor);
            byte[] albumCover = (byte[]) hashMap.get(albumCoverDescriptor);
            String albumCoverFormat = (String) hashMap.get(albumCoverFormatDescriptor);
            String data = (String) hashMap.get(dataDescriptor);
            String year = (String) hashMap.get(yearDescriptor);
            String genre = (String) hashMap.get(genreDescriptor);
            long duration = (Long) hashMap.get(durationDescriptor);
            String bmp = (String) hashMap.get(bmpDescriptor);
            return Optional.of(new TrackInfo(name, artist, album, albumCover, albumCoverFormat, data, year, genre, bmp, duration));
        } catch (ClassCastException e) {
            return Optional.empty();
        }
    }

    /**
     * creates a TrackInfo from the resourceModel
     * @param resourceModel the resourceModel
     * @return the optional TrackInfo
     */
    public static Optional<TrackInfo> importFromResource(ResourceModel resourceModel) {
        Object resource = resourceModel.getResource();
        try {
            @SuppressWarnings("unchecked")
            HashMap<String, Object> hashMap = (HashMap<String, Object>) resource;
            return importFromHashMap(hashMap);
        } catch (ClassCastException e) {
            return Optional.empty();
        }
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof TrackInfo)) return false;

        TrackInfo trackInfo = (TrackInfo) o;

        if (name != null ? !name.equals(trackInfo.name) : trackInfo.name != null) return false;
        if (artist != null ? !artist.equals(trackInfo.artist) : trackInfo.artist != null) return false;
        if (album != null ? !album.equals(trackInfo.album) : trackInfo.album != null) return false;
        if (year != null ? !year.equals(trackInfo.year) : trackInfo.year != null) return false;
        if (genre != null ? !genre.equals(trackInfo.genre) : trackInfo.genre != null) return false;
        if (bmp != null ? !bmp.equals(trackInfo.bmp) : trackInfo.bmp != null) return false;
        return !(duration > 0l ? duration != trackInfo.duration : trackInfo.duration < 0);

    }

    @Override
    public int hashCode() {
        int result = name != null ? name.hashCode() : 0;
        result = 31 * result + (artist != null ? artist.hashCode() : 0);
        result = 31 * result + (album != null ? album.hashCode() : 0);
        result = 31 * result + (year != null ? year.hashCode() : 0);
        result = 31 * result + (genre != null ? genre.hashCode() : 0);
        result = 31 * result + (bmp != null ? bmp.hashCode() : 0);
        result = (int) (31 * result + (duration > 0 ? duration : 0));
        return result;
    }
}


/*
 * Copyright 2010 Workplace Systems PLC (http://www.workplacesystems.com/).
 *
 * Licensed 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 com.workplacesystems.utilsj.collections;

import java.util.Hashtable;
import java.util.Map;
/**
 * A map builder in the ToStringBuilder style.  The {@link #put(Object, Object)}
 * calls can be chained, for example:
 *

 * Map<String,String> capital = new MapBuilder<String,String>()
 *      .put("Angola","Luanda")
 *      .put("Vietnam", "Hanoi")
 *      .put("Paraguay", "Asuncion")
 *      .put("Sweden", "Stockholm")
 *      .toMap();
 *

 * Some multiple puts are directly implemented, and more can easily be added:
 *

 * Map<String,String> currency = new MapBuilder<String,String>()
 *      .put("Peru", "Sol", "Brazil", "Real", "China", "Renminbi", "Vietnam", "Dong")
 *      .toMap();
 *

 *
 */
public class MapBuilder<K,V>
{
    private Map<K,V> map;

    /** new builder which uses supplied concrete map */
    public MapBuilder(Map<K,V> map)
    {
        this.map = map;
    }

    /** new builder which uses default (empty) concrete map */
    public MapBuilder()
    {
        this.map = new Hashtable<K, V>();
    }

    /** the built map */
    public Map<K,V> toMap()
    {
        return map;
    }

    public MapBuilder<K,V> put(K key, V value)
    {
        map.put(key, value);
        return this;
    }

    public MapBuilder<K,V> put(K key1, V value1, K key2, V value2)
    {
        map.put(key1, value1);
        map.put(key2, value2);
        return this;
    }

    public MapBuilder<K,V> put(K key1, V value1, K key2, V value2, K key3, V value3)
    {
        map.put(key1, value1);
        map.put(key2, value2);
        map.put(key3, value3);
        return this;
    }

    public MapBuilder<K,V> put(K key1, V value1, K key2, V value2, K key3, V value3, K key4, V value4)
    {
        map.put(key1, value1);
        map.put(key2, value2);
        map.put(key3, value3);
        map.put(key4, value4);
        return this;
    }

    /** a new map with just [key-->value] maplet */
    public static<K,V> Map<K,V> map(K key, V value)
    {
        return new MapBuilder().put(key,value).toMap();
    }

    /** a new map with just [key1-->value1, key2-->value2] maplet */
    public static<K,V> Map<K,V> map(K key1, V value1, K key2, V value2)
    {
        return new MapBuilder().put(key1,value1,key2,value2).toMap();
    }
}


/* Copyright 2014 Sven van der Meer <vdmeer.sven@mykolab.com>
 *
 * Licensed 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 de.vandermeer.execs;

import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.text.StrTokenizer;
import org.stringtemplate.v4.ST;
import org.stringtemplate.v4.STGroupFile;

import de.vandermeer.execs.cf.CF;

/**
 * The application executor.
 *
 * @author     Sven van der Meer <vdmeer.sven@mykolab.com>
 * @version    v0.4.0 build 170413 (13-Apr-17) for Java 1.8
 * @since      v0.0.1
 */
public class ExecS {
	/** Name of the application for help/usage and printouts. */
	String appName = "execs";

	/** Filter for jar files to be considered during search, this can speed up search process. */
	List<String> jarFilter = null;

	/** Filter for package names, this can speed up search process. */
	String packageFilter = null;

	/**
	 * Map of pre-registered applications.
	 * Use {@link #addApplication(String, Class)} to add your own applications.
	 */
	protected final TreeMap<String, Class<? extends ExecS_Application>> classmap;

	/** Application version, should be same as the version in the class JavaDoc. */
	public final static String APP_VERSION = "v0.4.0 build 170413 (13-Apr-17) for Java 1.8";

	/** Set of all classes filled during runtime search */
	final TreeSet<String> classNames;

	/** Local ST Group for printouts. */
	final STGroupFile stg;

	/**
	 * Standard constructor as used via direct command line execution.
	 */
	public ExecS(){
		this(null);
	}

	/**
	 * Constructor for sub-classes that wish to overwrite the application name.
	 * @param appName name of the application
	 */
	public ExecS(String appName){
		this.appName = (appName==null)?this.appName:appName;

		this.classmap = new TreeMap<String, Class<? extends ExecS_Application>>();
		this.classNames = new TreeSet<String>();

		this.addApplication(Gen_RunScripts.APP_NAME, Gen_RunScripts.class);
		this.addApplication(Gen_ConfigureSh.APP_NAME, Gen_ConfigureSh.class);
		this.addApplication(Gen_ExecJarScripts.APP_NAME, Gen_ExecJarScripts.class);

		this.stg = new STGroupFile("de/vandermeer/execs/execs.stg");
	}

	/**
	 * Returns a set of names that have been registered as applications with associated execution applications.
	 * @return list of names for registered execution applications, empty list if none registered
	 */
	public Set<String> getRegisteredApplications(){
		return this.classmap.keySet();
	}

	/**
	 * Sets the jar filter for the executor.
	 * This filter can contain any name of a jar file that should be considered during search.
	 * If not set, no filter is applied. If the argument is null, no filter is used. Otherwise, all
	 * strings in the argument are used as jar files and only those jar files will be used in a search.
	 * Using filters can speed up the search process.
	 * Filters can be set any time, a new filter will overwrite an existing one (using null will disable filtering).
	 * @param filter new array of jar file names for filters, null means do not use any filter.
	 */
	protected final void setJarFilter(String[] filter){
		this.jarFilter = (filter==null)?null:Collections.unmodifiableList(Arrays.asList(filter));
	}

	/**
	 * Sets the filter for package names.
	 * The string is used using a "starts-with" comparison of any package name during the search process.
	 * A filter set to "null" means no package filter is applied.
	 * Filters can be set any time, a new filter will overwrite an existing one (using null will disable filtering).
	 * @param filter package filter, null means no filter will be applied
	 */
	protected final void setPackageFiler(String filter){
		this.packageFilter = filter;
	}

	/**
	 * Adds a new application at runtime, with name (shortcut to start) and related class.
	 * @param name a unique name for the application
	 * @param clazz the class of the application for instantiation
	 */
	protected final void addApplication(String name, Class<? extends ExecS_Application> clazz){
		this.classmap.put(name, clazz);
	}

	/**
	 * Adds a set of application at runtime, as in all found applications that can be executed
	 * @param set a set of applications
	 */
	protected final void addAllApplications(Set<Class<?>> set){
		for(Class<?> cls:set){
			if(!cls.isInterface() && !Modifier.isAbstract(cls.getModifiers())){
				if(!this.classmap.containsValue(cls)){
					this.classNames.add(cls.getName());
				}
			}
		}
	}

	/**
	 * Main method
	 * @param args command line arguments
	 * @return 0 on success, -1 on error with message on STDERR
	 */
	public final int execute(String[] args){
		String arguments = StringUtils.join(args, ' ');
		StrTokenizer tokens = new StrTokenizer(arguments);

		String arg = tokens.nextToken();
		int ret = 0;

		if(arg==null || "-?".equals(arg) || "-h".equals(arg) || "--help".equals(arg)){
			//First help: no arguments or -? or --help -> print usage and exit(0)
			this.printUsage();
			return 0;
		}
		else if("-v".equals(arg) || "--version".equals(arg)){
			System.out.println(this.appName + " - " + ExecS.APP_VERSION);
			System.out.println();
		}
		else if("-l".equals(arg) || "--list".equals(arg)){
			//Second list: if -l or --list -> trigger search and exit(0)
			CF cf = new CF()
				.setJarFilter((ArrayUtils.contains(args, "-j"))?this.jarFilter:null)
				.setPkgFilter((ArrayUtils.contains(args, "-p"))?this.packageFilter:null);
			this.addAllApplications(cf.getSubclasses(ExecS_Application.class));
			this.printList();
			return 0;
		}
		else{
			Object svc = null;
			if(this.classmap.containsKey(arg)){
				try{
					svc = this.classmap.get(arg).newInstance();
				}
				catch(IllegalAccessException | InstantiationException iex){
					System.err.println(this.appName + ": tried to execute <" + args[0] + "> by registered name -> exception: " + iex.getMessage());
//					iex.printStackTrace();
					ret = -99;
				}
			}
			else{
				try{
					Class<?> c = Class.forName(arg);
					svc = c.newInstance();
				}
				catch(ClassNotFoundException | IllegalAccessException | InstantiationException ex){
					System.err.println(this.appName + ": tried to execute <" + args[0] + "> as class name -> exception: " + ex.getMessage());
//					ex.printStackTrace();
					ret = -99;
				}
			}
			ret = this.executeApplication(svc, args, arg);
		}

		if(ret==-99){
			//now we are in trouble, nothing we could do worked, so print that and quit
			System.err.println(this.appName + ": no application could be started and nothing else could be done, try '-?' or '--help' for help");
			System.err.println();
		}
		return ret;
	}

	/**
	 * Executes an application.
	 * @param svc the application, must not be null
	 * @param args original command line arguments
	 * @param orig the original name or class for the application for error messages
	 * @return 0 on success, other than zero on failure (including failure of the executed application)
	 */
	protected int executeApplication(Object svc, String[] args, String orig){
		if(svc!=null && (svc instanceof ExecS_Application)){
			if(svc instanceof Gen_RunScripts){
				//hook for GenRunScripts to get current class map - registered applications
				((Gen_RunScripts)svc).setClassMap(this.classmap);
			}
			if(svc instanceof Gen_ExecJarScripts){
				//hook for Gen_ExecJarScripts to get current class map - registered applications
				((Gen_ExecJarScripts)svc).setClassMap(this.classmap);
			}
			return ((ExecS_Application)svc).executeApplication(ArrayUtils.remove(args, 0));
		}
		else if(svc==null){
			System.err.println("could not create object for class or application name <" + orig + ">");
			return -1;
		}
		else if(!(svc instanceof ExecS_Application)){
			System.err.println("given class or application name <" + orig + "> is not instance of " + ExecS_Application.class.getName());
			return -2;
		}
		else{
			System.err.println("unexpected error processing for class or application name <" + orig + ">");
			return -3;
		}
	}

	/**
	 * Prints a list of pre-registered and found applications.
	 */
	protected final void printList(){
		ST list = this.stg.getInstanceOf("list");
		list.add("appName", this.appName);
		if(this.classmap.size()>0){
			List<Map<String, String>> l = new ArrayList<>();
			for(String key : this.classmap.keySet()){
				Map<String, String> m = new HashMap<>();
				m.put("key", key);
				m.put("val", this.classmap.get(key).getName());
				l.add(m);
			}
			list.add("classMap", l);
		}
		list.add("className", this.classNames);
		System.out.println(list.render());
	}

	/**
	 * Prints usage information to standard out.
	 */
	protected final void printUsage(){
		ST usage = this.stg.getInstanceOf("usage");
		usage.add("appName", this.appName);
		usage.add("packageFilter", this.packageFilter);
		usage.add("jarFilter", this.jarFilter);
		usage.add("excludedNames", new TreeSet<>(Arrays.asList(new CF().excludedNames)));
		System.out.println(usage.render());
	}

	/**
	 * Returns the set application name for the executor.
	 * This name is used for print outs only.
	 * @return set application name
	 */
	public final String getAppName(){
		return this.appName;
	}

	/**
	 * Public main to start the application executor.
	 * @param args command line arguments
	 */
	public static void main(String[] args) {
		ExecS run = new ExecS();
		int ret = run.execute(args);
		System.exit(ret);
	}

}


package org.intellimate.izou.sdk.frameworks.permanentSoundOutput.events;

import org.intellimate.izou.identification.Identification;
import org.intellimate.izou.sdk.events.CommonEvents;
import org.intellimate.izou.sdk.events.Event;
import org.intellimate.izou.sdk.frameworks.common.resources.SelectorResource;
import org.intellimate.izou.system.sound.SoundIDs;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Optional;

/**
 * signals that every Sound-Output should mute
 * @author LeanderK
 * @version 1.0
 */
public class MuteEvent extends Event {
    public static final String ID = SoundIDs.MuteEvent.descriptor;
    /**
     * Creates a new Event Object
     *
     * @param source      the source of the Event, most likely a this reference.
     * @throws IllegalArgumentException if one of the Arguments is null or empty
     */
    protected MuteEvent(Identification source)
            throws IllegalArgumentException {
        super(CommonEvents.Type.RESPONSE_TYPE, source, new ArrayList<>(Arrays.asList(ID,
                CommonEvents.Descriptors.NOT_INTERRUPT)));
    }

    /**
     * creates a new MuteEvent
     * @param source the caller
     * @param target the target who should start playing
     * @return the optional StartMusicRequest
     */
    public static Optional<MuteEvent> createMuteEvent(Identification source, Identification target) {
        if (target == null || target.equals(source))
            return Optional.empty();
        try {
            MuteEvent muteRequest = new MuteEvent(source);
            muteRequest.addResource(new SelectorResource(source, target));
            return Optional.of(muteRequest);
        } catch (IllegalArgumentException e) {
            return Optional.empty();
        }
    }

    /**
     * creates a new MuteEvent, will mute everything
     * @param source the caller
     * @return the optional StartMusicRequest
     */
    public static Optional<MuteEvent> createMuteEvent(Identification source) {
        if (source == null)
            return Optional.empty();
        try {
            MuteEvent muteRequest = new MuteEvent(source);
            return Optional.of(muteRequest);
        } catch (IllegalArgumentException e) {
            return Optional.empty();
        }
    }
}


/*
 * Copyright 2010-2011 Ning, Inc.
 *
 * Ning 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 com.ning.metrics.goodwill.access;

import com.mogwee.executors.Executors;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

public class CachingGoodwillAccessor extends Accessor
{
    private static final int DEFAULT_CACHE_TIMEOUT_IN_SECONDS = 90;
    private final Object cacheMonitor = new Object();

    private final ConcurrentHashMap<String, GoodwillSchema> knownSchemata = new ConcurrentHashMap<String, GoodwillSchema>();
    private final GoodwillAccessor delegate;

    public CachingGoodwillAccessor(final String host, final int port)
    {
        this(host, port, DEFAULT_CACHE_TIMEOUT_IN_SECONDS);
    }

    public CachingGoodwillAccessor(final String host, final int port, final int cacheTimeoutInSeconds)
    {
        super(host, port);

        delegate = new GoodwillAccessor(host, port);

        Executors.newScheduledThreadPool(1, "GoodwillCacheRefresher").scheduleWithFixedDelay(new Runnable()
        {
            @Override
            public void run()
            {
                refreshSchemataCache();
            }
        }, 0, cacheTimeoutInSeconds, TimeUnit.SECONDS);
    }

    /**
     * Invalidate and refresh the cache
     * <p/>
     * This is blocking and returns when the cache has been updated
     */
    public void refreshSchemataCache()
    {
        try {
            final List<GoodwillSchema> schemata = delegate.getSchemata().get();

            // If Goodwill is down - keep the old cache around
            if (schemata == null) {
                return;
            }

            final Map<String,GoodwillSchema> newSchemataCache = new ConcurrentHashMap<String, GoodwillSchema>();
            for (final GoodwillSchema schema : schemata) {
                newSchemataCache.put(schema.getName(), schema);
            }

            synchronized (cacheMonitor) {
                knownSchemata.clear();
                knownSchemata.putAll(newSchemataCache);
            }
        }
        catch (InterruptedException e) {
            log.warn("Interrupted while refreshing the cache");
            Thread.currentThread().interrupt();
        }
        catch (ExecutionException e) {
            log.warn("Unable to refresh schemata cache: {}", e.getLocalizedMessage());
        }
    }

    /**
     * Given a schema name, get the associated GoodwillSchema. This method tries hard to find it, i.e. it will refresh the
     * cache if the schema is not in the cache.
     *
     * @param schemaName name of the schema to find
     * @return GoodwillSchema describing the schema
     */
    public GoodwillSchema getSchema(final String schemaName)
    {
        GoodwillSchema schema = knownSchemata.get(schemaName);
        if (schema == null) {
            refreshSchemataCache();
            schema = knownSchemata.get(schemaName);
        }

        return schema;
    }

    public List<GoodwillSchema> getSchemata()
    {
        return Collections.list(knownSchemata.elements());
    }

    /**
     * Close the underlying http client
     */
    public synchronized void close()
    {
        delegate.close();
    }
}


/* Copyright 2017 Sven van der Meer <vdmeer.sven@mykolab.com>
 *
 * Licensed 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 de.vandermeer.execs;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.jar.Attributes;
import java.util.jar.Manifest;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.SystemUtils;

import de.vandermeer.execs.options.ApplicationOption;
import de.vandermeer.execs.options.ExecS_CliParser;

/**
 * Application to generate starts scripts when running ExecS from an executable JAR.
 *
 * @author     Sven van der Meer <vdmeer.sven@mykolab.com>
 * @version    v0.4.0 build 170413 (13-Apr-17) for Java 1.8
 * @since      v0.4.0
 */
public class Gen_ExecJarScripts implements ExecS_Application {

	/** Application name. */
	public final static String APP_NAME = "gen-exec-jar-scripts";

	/** Application display name. */
	public final static String APP_DISPLAY_NAME = "Generate JAR Scripts for Executable JARs";

	/** Application version, should be same as the version in the class JavaDoc. */
	public final static String APP_VERSION = "v0.4.0 build 170413 (13-Apr-17) for Java 1.8";

	/** CLI parser. */
	final private ExecS_CliParser cli;

	/** List of application options. */
	final private ArrayList<ApplicationOption<?>> options = new ArrayList<>();

	/** Local class map, must be set by calling ExecS instance. */
	Map<String, Class<? extends ExecS_Application>> execClassMap;

	/**
	 * Creates a new application.
	 */
	public Gen_ExecJarScripts(){
		this.cli = new ExecS_CliParser();
	}

	@Override
	public int executeApplication(String[] args) {
		int ret = ExecS_Application.super.executeApplication(args);
		if(ret!=0){
			return ret;
		}

		if(!this.inExecJar()){
			return -1;
		}

		String jarFn;
		try {
			File file = new File(Gen_ExecJarScripts.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath());
			jarFn = file.toString();
		}
		catch (URISyntaxException ex) {
			System.err.println(this.getAppName() + ": error retrieving JAR path - " + ex.getMessage());
			return -2;
		}

		if(jarFn==null){
			System.err.println(this.getAppName() + ": could not retrieve JAR path");
			return -3;
		}

		if(!SystemUtils.IS_OS_UNIX && !SystemUtils.IS_OS_WINDOWS){
			System.err.println(this.getAppName() + ": OS not supported, neither Unix nor Windows");
			return -4;
		}

		ArrayList<String> cmds = new ArrayList<>();
		if(this.execClassMap!=null){
			for(String s : execClassMap.keySet()){
				if(s.equals(Gen_ConfigureSh.APP_NAME)){
					continue;
				}
				if(s.equals(Gen_RunScripts.APP_NAME)){
					continue;
				}
				if(s.equals(APP_NAME)){
					continue;
				}
				cmds.add(s);
			}
		}

		String dir = System.getProperty("user.dir");
		for(String s : cmds){
			if(SystemUtils.IS_OS_UNIX){
				String fn = dir + "/" + s + ".sh";
				File file = new File(fn);
				List<String> lines = new ArrayList<>();
				lines.add("#!/usr/bin/env bash");
				lines.add("");
				lines.add("java -jar " + jarFn + " " + s + " $*");
				lines.add("");
				if(!this.writeFile(file, lines)){
					return -5;
				}
			}
			else if(SystemUtils.IS_OS_WINDOWS){
				String fn = dir + "/" + s + ".bat";
				File file = new File(fn);
				List<String> lines = new ArrayList<>();
				lines.add("@echo off");
				lines.add("");
				lines.add("java -jar " + jarFn + " " + s + " %*");
				lines.add("");
				if(!this.writeFile(file, lines)){
					return -6;
				}
			}
		}

		return 0;
	}

	/**
	 * Writes the given lines to the given file, if possible.
	 * @param file file to write to
	 * @param lines lines to write to the file
	 * @return true if lines where written to the file, false otherwise with errors printed
	 */
	public boolean writeFile(File file, List<String> lines){
		if(file.exists()){
			file.delete();
		}
		try {
			FileWriter out = new FileWriter(file);
			for(String s : lines){
				out.write(s);
				out.write(System.getProperty("line.separator"));
			}
			out.close();
			file.setExecutable(true);
		}
		catch (IOException ex) {
			System.err.println(this.getAppName() + ": IO exception while writing to file - " + file + " with message: " + ex.getMessage());
			return false;
		}
		return true;
	}

	/**
	 * Tests if the class is run from an executable JAR.
	 * @return true if run from an executable JAR (JAR with Main-Class in manifest), false otherwise with errors printed
	 */
	protected boolean inExecJar(){
		Class<Gen_ExecJarScripts> clazz = Gen_ExecJarScripts.class;
		String className = clazz.getSimpleName() + ".class";
		String classPath = clazz.getResource(className).toString();
		if (!classPath.startsWith("jar")) {
			System.err.println(this.getAppName() + ": not started in a jar, cannot proceed");
			return false;
		}

		String manifestPath = classPath.substring(0, classPath.lastIndexOf("!") + 1) + "/META-INF/MANIFEST.MF";
		Manifest manifest;
		try {
			manifest = new Manifest(new URL(manifestPath).openStream());
		}
		catch (IOException ex) {
			System.err.println(this.getAppName() + ": exception while retrieving manifest: " + ex.getMessage());
			return false;
		}
		Attributes attr = manifest.getMainAttributes();
		if(StringUtils.isBlank(attr.getValue("Main-Class"))){
			System.err.println(this.getAppName() + ": no main class in manifest, probably not an executable JAR, cannot continue");
			return false;
		}
		return true;
	}

	/**
	 * Adds a new option to CLI parser and option list.
	 * @param option new option, ignored if null
	 */
	protected void addOption(ApplicationOption<?> option){
		if(option!=null){
			this.getCli().addOption(option);
			this.options.add(option);
		}
	}

	@Override
	public String getAppDescription() {
		return "Generates scripts (OS specific) for running S2V applications from the JAR with all dependencies";
	}

	@Override
	public String getAppDisplayName(){
		return APP_DISPLAY_NAME;
	}

	@Override
	public String getAppName() {
		return APP_NAME;
	}

	@Override
	public ApplicationOption<?>[] getAppOptions() {
		return this.options.toArray(new ApplicationOption<?>[]{});
	}

	@Override
	public String getAppVersion() {
		return APP_VERSION;
	}

	@Override
	public ExecS_CliParser getCli() {
		return this.cli;
	}

	/**
	 * Hook for a calling ExecS instance to set its class map for the script generator
	 * @param execClassMap calling executor class map to create run scripts from
	 */
	public void setClassMap(Map<String, Class<? extends ExecS_Application>> execClassMap){
		this.execClassMap = execClassMap;
	}
}


/*
 * Copyright 2010-2011 Ning, Inc.
 *
 * Ning 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 com.ning.metrics.goodwill.access;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonValue;
import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.ImmutableMap;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;

/**
 * Describe a Schema in Goodwill.
 * This is basically a union of a Schema and extra metadata for the Sink.
 *
 * @see com.ning.metrics.serialization.schema.Schema
 */
public class GoodwillSchema
{
    private static final ObjectMapper mapper = new ObjectMapper();

    private final String name;
    private String sinkAddInfo;
    private final HashMap<Short, GoodwillSchemaField> thriftItems = new HashMap<Short, GoodwillSchemaField>();

    public static final String JSON_THRIFT_TYPE_NAME = "name";
    public static final String JSON_THRIFT_TYPE_SCHEMA = "schema";
    public static final String JSON_THRIFT_TYPE_SINK_ADD_INFO = "sinkAddInfo";

    /**
     * Jackson constructor
     * <p/>
     * {
     * "sinkAddInfo": null,
     * "name": "hello",
     * "schema": [
     * {
     * "name": "my hello attribute",
     * "type": "string",
     * "position": 1,
     * "description": "awesome attribute",
     * "sql": {
     * "type": "nvarchar",
     * "length": null,
     * "scale": null,
     * "precision": null
     * }
     * },
     * {
     * "name": "dsfdfsfds",
     * "type": "bool",
     * "position": 2,
     * "description": "dfsfdsfds",
     * "sql": {
     * "type": "boolean",
     * "length": null,
     * "scale": null,
     * "precision": null
     * }
     * },
     * {
     * "name": "wer",
     * "type": "double",
     * "position": 3,
     * "description": "wer",
     * "sql": {
     * "type": "numeric",
     * "length": null,
     * "scale": 12,
     * "precision": 42
     * }
     * }
     * ]
     * }
     *
     * @param name        Schema name
     * @param items       List of fields
     * @param sinkAddInfo extra information for the Sink
     */
    @JsonCreator
    public GoodwillSchema(
        @JsonProperty(JSON_THRIFT_TYPE_NAME) final String name,
        @JsonProperty(JSON_THRIFT_TYPE_SCHEMA) final List<GoodwillSchemaField> items,
        @JsonProperty(JSON_THRIFT_TYPE_SINK_ADD_INFO) final String sinkAddInfo
    )
    {
        this(name, items);
        setSinkAddInfo(sinkAddInfo);
    }

    /**
     * Manual constructor, typically used by Goodwill stores.
     *
     * @param name  Schema name
     * @param items List of fields
     */
    public GoodwillSchema(final String name, final List<GoodwillSchemaField> items)
    {
        this.name = name;
        for (final GoodwillSchemaField field : items) {
            addThriftField(field);
        }
    }

    public static GoodwillSchema decode(final String thriftJson) throws IOException
    {
        return mapper.readValue(thriftJson, GoodwillSchema.class);
    }

    @JsonValue
    @SuppressWarnings({"unchecked"})
    public ImmutableMap toMap()
    {
        return new ImmutableMap.Builder()
            .put(JSON_THRIFT_TYPE_NAME, getName())
            .put(JSON_THRIFT_TYPE_SCHEMA, getSchema())
            .put(JSON_THRIFT_TYPE_SINK_ADD_INFO, sinkAddInfo == null ? "" : sinkAddInfo)
            .build();
    }

    /**
     * Add a field in the Thrift. The code does not enforce sanity w.r.t. field positions.
     *
     * @param goodwillSchemaField field to add
     */
    public void addThriftField(final GoodwillSchemaField goodwillSchemaField)
    {
        thriftItems.put(goodwillSchemaField.getId(), goodwillSchemaField);
    }

    public String getName()
    {
        return name;
    }

    /**
     * Get the schema as a collection of fields.
     * We guarantee the ordering by field id.
     *
     * @return the sorted collection of fields
     */
    public ArrayList<GoodwillSchemaField> getSchema()
    {
        final ArrayList<GoodwillSchemaField> items = new ArrayList<GoodwillSchemaField>(thriftItems.values());

        Collections.sort(items, new Comparator<GoodwillSchemaField>()
        {
            @Override
            public int compare(final GoodwillSchemaField left, final GoodwillSchemaField right)
            {
                return Short.valueOf(left.getId()).compareTo(right.getId());
            }
        });

        return items;
    }

    public void setSinkAddInfo(final String sinkAddInfo)
    {
        this.sinkAddInfo = sinkAddInfo;
    }

    /**
     * Given a position, return the field at that position.
     *
     * @param i position in the Thrift (start with 1)
     * @return the GoodwillSchemaField object
     */
    public GoodwillSchemaField getFieldByPosition(final short i)
    {
        return thriftItems.get(i);
    }

    /**
     * Given a name, return the field matching the name.
     *
     * @param name GoodwillSchemaField name
     * @return the GoodwillSchemaField object
     */
    public GoodwillSchemaField getFieldByName(final String name)
    {
        for (final GoodwillSchemaField field : thriftItems.values()) {
            if (field.getName().equals(name)) {
                return field;
            }
        }

        return null;
    }

    @Override
    public String toString()
    {
        try {
            return mapper.writeValueAsString(this);
        }
        catch (JsonGenerationException e) {
            return "GoodwillSchema{" +
                JSON_THRIFT_TYPE_NAME + "='" + getName() + '\'' +
                ", thriftItems=" + getSchema() +
                '}';
        }
        catch (IOException e) {
            return "GoodwillSchema{" +
                JSON_THRIFT_TYPE_NAME + "='" + getName() + '\'' +
                ", thriftItems=" + getSchema() +
                '}';
        }
    }

    public byte[] toJSONBytes() throws IOException
    {
        return mapper.writeValueAsBytes(this);
    }

    /**
     * @deprecated Use 'toJSONBytes()' instead
     */
    @Deprecated
    public ByteArrayOutputStream toJSON() throws IOException
    {
        // silly, but
        final byte[] bytes = toJSONBytes();
        final ByteArrayOutputStream out = new ByteArrayOutputStream(bytes.length);
        out.write(bytes);
        return out;
    }
}


/* Copyright 2014 Sven van der Meer <vdmeer.sven@mykolab.com>
 *
 * Licensed 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 de.vandermeer.execs.cf;

import java.io.File;
import java.io.FileFilter;
import java.util.jar.JarEntry;

/**
 * Class finder utilities.
 *
 * @author     Sven van der Meer <vdmeer.sven@mykolab.com>
 * @version    v0.4.0 build 170413 (13-Apr-17) for Java 1.8
 * @since      v0.0.6
 */
public abstract class CF_Utils {

	/**
	 * A simple filter for directories.
	 * The filter returns true if the given file exists and is a directory, false otherwise.
	 */
	public final static FileFilter DIRECTORIES_ONLY = new FileFilter(){
		public boolean accept (File f){
			if (f.exists() && f.isDirectory()){
				return true;
			}
			return false;
		}
	};

	/**
	 * Returns the package name for a jar entry.
	 * @param entry jar entry to extract package name from
	 * @return empty string on error (entry being null, or no name), package name otherwise
	 */
	public final static String getPkgName(JarEntry entry){
		if(entry==null){
			return "";
		}
		String s = entry.getName();
		if(s==null){
			return "";
		}
		if(s.length()==0){
			return s;
		}
		if(s.startsWith("/")){
			s = s.substring(1, s.length());
		}
		if(s.endsWith("/")){
			s = s.substring(0, s.length()-1);
		}
		return s.replace('/', '.');
	}

}


/*
 * Licensed to the Sakai Foundation (SF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The SF 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 uk.co.tfd.sm.proxy;

import org.apache.http.Header;
import org.apache.http.HeaderElement;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.collect.Lists;

import uk.co.tfd.sm.api.proxy.ProxyResponse;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Implements the ProxyResponse holder by wrapping the HttpMethod request.
 */
public class ProxyResponseImpl implements ProxyResponse {

	private static final Logger LOGGER = LoggerFactory.getLogger(ProxyResponseImpl.class);
	private HttpResponse response;
	private Map<String, String[]> headers = new HashMap<String, String[]>();
	private List<InputStream> leakedInputStreams = Lists.newArrayList();
	private int code;
	private String responseCause;

	/**
	 * @param result
	 * @param method
	 */
	public ProxyResponseImpl(HttpResponse response) {
		this.response = response;
		code = response.getStatusLine().getStatusCode();
		responseCause = response.getStatusLine().getReasonPhrase();

		for (org.apache.http.Header header : response.getAllHeaders()) {
			String name = header.getName();
			String[] values = headers.get(name);
			if (values == null) {
				values = new String[] { header.getValue() };
			} else {
				String[] newValues = new String[values.length + 1];
				System.arraycopy(values, 0, newValues, 0, values.length);
				newValues[values.length] = header.getValue();
				values = newValues;
			}

			boolean add = true;
			// We ignore JSESSIONID cookies coming back.
			if (name.toLowerCase().equals("set-cookie")) {
				for (String v : values) {
					if (v.contains("JSESSIONID")) {
						add = false;
						break;
					}
				}
			}
			if (add) {
				headers.put(name, values);
			}
		}
	}

	public ProxyResponseImpl(int responseCode, String responseMessage,
			HttpResponse response) {
		this(response);
		code = responseCode;
		responseCause = responseMessage;
	}

	/**
	 * {@inheritDoc}
	 *
	 * @see uk.co.tfd.sm.api.proxy.ProxyResponse#getResultCode()
	 */
	public int getResultCode() {
		return code;
	}

	/**
	 * {@inheritDoc}
	 *
	 * @see uk.co.tfd.sm.api.proxy.ProxyResponse#getResponseHeaders()
	 */
	public Map<String, String[]> getResponseHeaders() {
		return headers;
	}

	/**
	 * {@inheritDoc}
	 *
	 * @see uk.co.tfd.sm.api.proxy.ProxyResponse#getResponseBodyAsInputStream()
	 */
	public InputStream getResponseBodyAsInputStream() throws IOException {
		InputStream in = response.getEntity().getContent();
		leakedInputStreams.add(in);
		return in;
	}

	/**
	 * {@inheritDoc}
	 *
	 * @see uk.co.tfd.sm.api.proxy.ProxyResponse#getResponseBodyAsString()
	 */
	public String getResponseBodyAsString() throws IOException {
		HttpEntity entity = response.getEntity();
		if ( entity == null ) {
			return null;
		}
		String contentEncoding = getContentEncoding(entity);
		InputStream in = entity.getContent();
		BufferedReader r = new BufferedReader(new InputStreamReader(in,
				contentEncoding));
		StringBuilder sb = new StringBuilder();
		for (;;) {
			String l = r.readLine();
			if (l == null) {
				break;
			}
			sb.append(l).append("\n");
		}
		r.close();
		in.close();
		return sb.toString();
	}

	private String getContentEncoding(HttpEntity entity) {
		String contentEncoding = null;
		if ( entity == null ) {
			return "UTF-8";
		}
		Header contentEncodingHeader = entity.getContentEncoding();
		if (contentEncodingHeader != null) {
			contentEncoding = contentEncodingHeader.getValue();
		}
		if (contentEncoding == null) {
			Header contentTypeHeader = entity.getContentType();
			if (contentTypeHeader != null) {
				HeaderElement[] entries = contentTypeHeader.getElements();
				for (HeaderElement h : entries) {
					NameValuePair charset = h.getParameterByName("charset");
					if (charset != null) {
						contentEncoding = charset.getValue();
						break;
					}
				}
			}
		}
		if (contentEncoding == null) {
			contentEncoding = "UTF-8";
		}
		return contentEncoding;
	}

	/**
	 * {@inheritDoc}
	 *
	 * @see uk.co.tfd.sm.api.proxy.ProxyResponse#close()
	 */
	public void close() {
		for ( InputStream in : leakedInputStreams) {
			try {
				in.close();
			} catch (IOException e) {
				LOGGER.debug(e.getMessage(),e);
			}
		}
	}

	/**
	 * @return the cause
	 */
	public String getCause() {
		return responseCause;
	}

}


package org.tenidwa.collections.utils;

import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;

/**
 * Map that uses function of K as keys instead of K.
 *

 * Its purpose is to allow mapping from keys that don't support
 * {@link Object#equals(Object)}. When using the usual {@link Map} that has an
 * interface as a key type, there is no sane way to treat instances of
 * different types as equal, because then we'd have to:
 *

 *
re-implement {@link Object#equals(Object)} in every type (which would
 *     be a lot of duplicated code);

 *
or extend every implementation of the interface with an abstract
 *     class that implements {@link Object#equals(Object)} using methods of that
 *     interface (which would still be duplicating code, not per interface
 *     implementation but per interface).

 *

 *

 * "Content" in "ContentMap" means "something equatable that is obtained with
 * public methods of interface {@code K}"
 *

 * For example, if {@code PersonWithoutEquals} and {@code PersonFromStream}
 * don't implement equals but have method {@code Person#name()} that returns
 * {@link String}, we could map {@code Person}s to something else like this:
 *

 *     Map<Person, Integer> map =
 *         new ContentMap<Person, String, Integer>(Person::name);
 *     map.put(new PersonWithoutEquals("Jeff"), 1);
 *     map.get(new PersonFromStream(streamWithNameJeff)); // 1
 *

 *

 * This map has deterministic ordering.
 *

 * This map doesn't support methods {@link Map#keySet()} and
 * {@link Map#entrySet()} because it doesn't store the keys of type
 * {@code K}, it just uses those to obtain the "hidden" keys of type {@code C}.
 *

 * This map is immutable, mutator methods will throw
 * {@link UnsupportedOperationException}.
 * @param <K> Key type
 * @param <C> Some content obtainable from K
 * @param <V> Value type
 * @author Georgy Vlasov (suseika@tendiwa.org)
 * @version $Id$
 * @since 0.6.0
 */
public final class ContentMap<K, C, V> implements Map<K, V> {
    /**
     * Map from content of the base map to its values.
     */
    private final transient ImmutableMap<C, V> map;

    /**
     * Function to extract content from keys.
     */
    private final transient Function<K, C> function;

    /**
     * Original map.
     */
    private final transient Map<K, V> original;

    /**
     * Ctor.
     * @param base Base map.
     * @param function Function to get content of a key.
     */
    public ContentMap(
        final ImmutableMap<K, V> base,
        final Function<K, C> function
    ) {
        this.function = function;
        this.map = this.mapContentToValues(base);
        this.original = base;
    }

    @Override
    public int size() {
        return this.map.size();
    }

    @Override
    public boolean isEmpty() {
        return this.map.isEmpty();
    }

    @Override
    public boolean containsKey(Object o) {
        return this.map.containsKey(this.key(o));
    }

    @Override
    public boolean containsValue(Object o) {
        return this.map.containsValue(o);
    }

    @Override
    public V get(Object o) {
        return this.map.get(this.key(o));
    }

    @Deprecated
    @Override
    public V put(K k, V v) {
        throw new UnsupportedOperationException(
            "Put operation is not supported"
        );
    }

    @Deprecated
    @Override
    public V remove(Object o) {
        throw new UnsupportedOperationException(
            "Remove operation is not supported"
        );
    }

    @Deprecated
    @Override
    public void putAll(Map<? extends K, ? extends V> map) {
        throw new UnsupportedOperationException(
            "Put all operation is not supported"
        );
    }

    @Deprecated
    @Override
    public void clear() {
        throw new UnsupportedOperationException(
            "Clear operation is not supported"
        );
    }

    @Override
    public Set<K> keySet() {
        return ImmutableSet.copyOf(this.original.keySet());
    }

    @Override
    public Collection<V> values() {
        return this.map.values();
    }

    @Override
    public Set<Entry<K, V>> entrySet() {
        return ImmutableSet.copyOf(this.original.entrySet());
    }

    @SuppressWarnings("unchecked")
    private C key(Object o) {
        return this.function.apply((K)o);
    }

    /**
     * Maps the content of the base map to its values.
     * @param base Base map
     * @return Map from content of the base map to its values.
     */
    private ImmutableMap<C, V> mapContentToValues(
        final ImmutableMap<K, V> base
    ) {
        final ImmutableMap.Builder<C, V> builder = ImmutableMap.builder();
        for (final Entry<K, V> entry : base.entrySet()) {
            builder.put(
                this.key(entry.getKey()),
                entry.getValue()
            );
        }
        return builder.build();
    }
}


/*
 * Licensed to the Sakai Foundation (SF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The SF 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.sakaiproject.nakamura.lite.storage.spi;

import com.google.common.collect.ImmutableSet;

import org.apache.commons.pool.PoolableObjectFactory;
import org.apache.commons.pool.impl.GenericObjectPool;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.Reference;
import org.sakaiproject.nakamura.api.lite.ClientPoolException;
import org.sakaiproject.nakamura.api.lite.Configuration;
import org.sakaiproject.nakamura.api.lite.StorageClientUtils;
import org.sakaiproject.nakamura.lite.storage.spi.types.LongString;
import org.sakaiproject.nakamura.lite.storage.spi.types.StringType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Map;
import java.util.Set;

@Component(componentAbstract = true)
public abstract class AbstractClientConnectionPool implements StorageClientPool {

    private static final Logger LOGGER = LoggerFactory
            .getLogger(AbstractClientConnectionPool.class);

    @Property(intValue = 200)
    private static final String MAX_ACTIVE = "max-active";
    @Property(longValue = 10)
    private static final String MAX_WAIT = "max-wait";
    @Property(intValue = 5)
    private static final String MAX_IDLE = "max-idle";
    @Property(boolValue = true)
    private static final String TEST_ON_BORROW = "test-on-borrow";
    @Property(boolValue = true)
    private static final String TEST_ON_RETURN = "test-on-return";
    @Property(longValue = 60000)
    private static final String TIME_BETWEEN_EVICTION_RUNS_MILLIS = "time-between-eviction-run";
    @Property(intValue = 1000)
    private static final String NUM_TESTS_PER_EVICTION_RUN = "num-tests-per-eviction-run";
    @Property(longValue = 10000)
    private static final String MIN_EVICTABLE_IDLE_TIME_MILLIS = "min-evictable-idle-time-millis";
    @Property(boolValue = false)
    private static final String TEST_WHILE_IDLE = "test-while-idle";
    @Property(value = "grow")
    private static final String WHEN_EHAUSTED = "when-exhausted-action";

    private static final int DEFAULT_LONG_STRING_SIZE = 16*1024;
    @Property(intValue = DEFAULT_LONG_STRING_SIZE)
    private static final String LONG_STRING_SIZE = "long-string-size";

    public  static final String DEFAULT_FILE_STORE = "store";
    @Property(value = DEFAULT_FILE_STORE)
    public static final String FS_STORE_BASE_DIR = "store-base-dir";

    /**
     * Is ok to store content and long strings in the same location as they are
     * identified by sha1 hashes and so unique. If a deplorer decides they want different
     * locations, they can configure.
     */
    @Property(value = DEFAULT_FILE_STORE)
    private static final String LONG_STRING_STORE_BASE = "long-string-base-dir";

    @Reference
    private Configuration configuration;

    private Set<String> indexColumns;

    private GenericObjectPool pool;

    private Set<String> indexColumnsTypes;

    public AbstractClientConnectionPool() {
    }

    @Activate
    public void activate(Map<String, Object> properties) throws ClassNotFoundException {
        // for testing purposes
        if ( configuration == null ) {
            configuration = (Configuration) properties.get(Configuration.class.getName());
        }
        indexColumns = ImmutableSet.copyOf(configuration.getIndexColumnNames());
        indexColumnsTypes = ImmutableSet.copyOf(configuration.getIndexColumnTypes());
        int maxActive = StorageClientUtils.getSetting(properties.get(MAX_ACTIVE), 200);
        byte whenExhaustedAction = GenericObjectPool.DEFAULT_WHEN_EXHAUSTED_ACTION;
        String whenExhausted = (String) properties.get(WHEN_EHAUSTED);
        if ("fail".equals(whenExhausted)) {
            whenExhaustedAction = GenericObjectPool.WHEN_EXHAUSTED_FAIL;
        } else if ("grow".equals(whenExhausted)) {
            whenExhaustedAction = GenericObjectPool.WHEN_EXHAUSTED_GROW;
        } else if ("block".equals(whenExhausted)) {
            whenExhaustedAction = GenericObjectPool.WHEN_EXHAUSTED_BLOCK;
        }
        long maxWait = StorageClientUtils.getSetting(properties.get(MAX_WAIT), 10L);
        int maxIdle = StorageClientUtils.getSetting(properties.get(MAX_IDLE), 5);
        boolean testOnBorrow = StorageClientUtils.getSetting(properties.get(TEST_ON_BORROW), true);
        boolean testOnReturn = StorageClientUtils.getSetting(properties.get(TEST_ON_RETURN), true);
        long timeBetweenEvictionRunsMillis = StorageClientUtils.getSetting(
                properties.get(TIME_BETWEEN_EVICTION_RUNS_MILLIS), 60000L);
        int numTestsPerEvictionRun = StorageClientUtils.getSetting(properties.get(NUM_TESTS_PER_EVICTION_RUN), 1000);
        long minEvictableIdleTimeMillis = StorageClientUtils.getSetting(
                properties.get(MIN_EVICTABLE_IDLE_TIME_MILLIS), 10000L);
        boolean testWhileIdle = StorageClientUtils.getSetting(properties.get(TEST_WHILE_IDLE), false);

        pool = new GenericObjectPool(getConnectionPoolFactory(), maxActive, whenExhaustedAction,
                maxWait, maxIdle, testOnBorrow, testOnReturn, timeBetweenEvictionRunsMillis,
                numTestsPerEvictionRun, minEvictableIdleTimeMillis, testWhileIdle);

        // set the maximum size of a string, if this is not 0, strings over this size will become files.
        StringType.setLengthLimit(StorageClientUtils.getSetting(properties.get(LONG_STRING_SIZE),DEFAULT_LONG_STRING_SIZE));
        // location of the long string store.
        LongString
                .setBase(StorageClientUtils.getSetting(properties.get(LONG_STRING_STORE_BASE),
                        StorageClientUtils.getSetting(properties.get(FS_STORE_BASE_DIR),
                                DEFAULT_FILE_STORE)));

    }

    protected abstract PoolableObjectFactory getConnectionPoolFactory();

    @Deactivate
    public void deactivate(Map<String, Object> properties) {
        try {
            pool.clear();
            pool.close();
            LOGGER.debug("Sparse Map Content client pool closed ");
        } catch (Exception e) {
            LOGGER.error("Failed to close pool ", e);
        }
    }

    public Set<String> getIndexColumns() {
        return indexColumns;
    }

    public Set<String> getIndexColumnsTypes() {
        return indexColumnsTypes;
    }

    /*
     * (non-Javadoc)
     *
     * @see
     * org.sakaiproject.nakamura.lite.cassandra.ConnectionPool#openConnection()
     */
    public StorageClient getClient() throws ClientPoolException {
        try {
            StorageClient client = (StorageClient) pool.borrowObject();
            LOGGER.debug("Borrowed storage client pool client:" + client);
            return client;
        } catch (Exception e) {
            LOGGER.warn("Failed To Borrow connection from pool {} ",e.getMessage());
            throw new ClientPoolException("Failed To Borrow connection from pool ", e);
        }
    }

    /*
     * (non-Javadoc)
     *
     * @see
     * org.sakaiproject.nakamura.lite.cassandra.ConnectionPool#closeConnection()
     */
    public void releaseClient(StorageClient client) {
        try {
            if (client != null) {
                pool.returnObject(client);
                LOGGER.debug("Released storage client pool client:" + client);
            }
        } catch (Exception e) {
            LOGGER.warn("Failed to close connection " + e.getMessage(), e);
        }
    }

}


package org.intellimate.izou.sdk.contentgenerator;

import org.intellimate.izou.events.EventModel;
import org.intellimate.izou.identification.Identifiable;
import org.intellimate.izou.identification.IdentificationManagerM;
import org.intellimate.izou.sdk.events.CommonEvents;
import org.intellimate.izou.sdk.events.Event;

import java.util.Collections;
import java.util.Optional;

/**
 * This class is used in the ContentGenerator to signal that a ContentGenerator wants to get triggered when it gets
 * fired.
 * @author Leander Kurscheidt
 * @version 1.0
 */
public class EventListener {
    private final Event event;
    private final String descriptor;
    private final String description;
    private final String descriptorID;

    /**
     * Creates a new EventListener.
     *
     * @param event the Event to associate with
     * @param descriptor the descriptor of the Event you want to listen to
     * @param description the description of the descriptor
     * @param descriptorID an ID for the descriptor (Should contain no special characters, spaces etc.)
     */
    EventListener(Event event, String descriptor, String description, String descriptorID) {
        this.event = event;
        this.descriptor = descriptor;
        this.description = description;
        this.descriptorID = descriptorID;
    }

    /**
     * Create the EventListener.
     *
     * @param descriptor the descriptor of the Event you want to listen to
     * @param description the description of the descriptor
     * @param descriptorID an ID for the descriptor (Should contain no special characters, spaces etc.). Only String
     *                     which match the regex (\w-_)+ are allowed.
     * @param identifiable the identifiable which wants to create the EventListener
     * @return if one of the parameter is null, or unable to to obtain ID
     * @throws java.lang.IllegalArgumentException if the descriptorID contains illegal characters
     */
    public static Optional<EventListener> createEventListener(String descriptor, String description,
                                                       String descriptorID, Identifiable identifiable)
                                                                                throws IllegalArgumentException{

        if (!descriptorID.matches("[\\w\\-_]+"))
            throw new IllegalArgumentException("descriptorID: " + descriptorID + " contains illegal characters");
        return
                IdentificationManagerM.getInstance().getIdentification(identifiable)
                .flatMap(id -> Event.createEvent(CommonEvents.Type.NOTIFICATION_TYPE, id, Collections.singletonList(descriptor)))
                .map(event -> new EventListener(event, descriptor, description, descriptorID));
    }

    /**
     * Returns the associated Event
     *
     * @return The Event
     */
    public EventModel<?> getEvent() {
        return event;
    }

    /**
     * Returns the descriptor of the Event you want to listen to
     *
     * @return a String
     */
    public String getDescriptor() {
        return descriptor;
    }

    /**
     * the description of the descriptor
     *
     * @return a String
     */
    public String getDescription() {
        return description;
    }

    /**
     * An ID for the descriptor (Should contain no special characters, spaces etc.)
     *
     * @return a String
     */
    public String getDescriptorID() {
        return descriptorID;
    }
}


/*
 * Copyright 2010 Workplace Systems PLC (http://www.workplacesystems.com/).
 *
 * Licensed 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 com.workplacesystems.utilsj.collections;

import java.util.Iterator;
import com.workplacesystems.utilsj.Callback;

import com.workplacesystems.utilsj.UtilsjException;
import com.workplacesystems.utilsj.collections.decorators.SynchronizedFilterableCollection;

/** IterativeCallback provides a means of threadsafe iteration on collections of records.
 *
 * @author  Dave
 */
abstract public class IterativeCallback<T,R>
{
    private IterativeState<T,R> state;

    /** Creates a new instance of IterativeCallback */
    public IterativeCallback()
    {
        initState();
    }

    /** Creates a new instance of IterativeCallback, accepting an object that may be amended throughout the iteration process */
    public IterativeCallback(R amended_object)
    {
        this();
        state.amended_object = amended_object;
    }

    private void initState()
    {
        // Only create a new state if one doesn't exist already.
        // newState and setState can be used if more state control is required.
        if (state == null)
            newState();
    }

    /**
     * Creates a new state for this IterativeCallback.
     *
     * @return previous state
     */
    public IterativeState<T,R> newState()
    {
        return setState(new IterativeState<T,R>());
    }

    /**
     * Sets the supplied state for this IterativeCallback.
     *
     * @return previous state
     */
    public IterativeState<T,R> setState(IterativeState<T,R> new_state)
    {
        IterativeState<T,R> old_state = this.state;
        this.state = new_state;
        return old_state;
    }

    /** public wrapper for the iteration */
    public R iterate(final FilterableCollection<? extends T> c)
    {
        initState();

        checkUsed();

        // If collection is decorated with a syncronized wrapper then synchronize the iteration
        if (c instanceof SynchronizedFilterableCollection)
        {
            return SyncUtils.synchronizeRead(c, new Callback<R>() {
                @Override
                protected void doAction()
                {
                    _return(doIteration(c.iterator()));
                }
            });
        }
        return doIteration(c.iterator());
    }

    /**
     * Find out if callback has been used.
     * @return true if used, false if not
     */
    protected boolean hasBeenUsed() { return state.iterative_callback_used; }

    /** check callback used only once */
    protected final void checkUsed()
    {
        if (state.iterative_callback_used)
            throw new UtilsjException("IterativeCallback can not be used more than once.");
        state.iterative_callback_used = true;
    }

    /** do the actual iteration */
    private R doIteration(Iterator<? extends T> it)
    {
        // save the iterator into member variable
        state.i = it;
        state.iterations = 0;

        if (state.do_break == true)
            return state.return_object;

        // do the iteration calling nextobject on each
        while (state.i.hasNext())
        {
            T o = state.i.next();

            if (delegate != null)
                delegate.delegate(o);
            else
                iterateObject(o);

            if (state.do_break == true)
                return state.return_object;
        }
        return state.amended_object;
    }

    void iterateObject(T o)
    {
        if (state.iterations == 0)
            firstObject(o);
        else
            nextObject(o);
        state.iterations++;
    }

    protected void firstObject(T o)
    {
        nextObject(o);
    }

    protected final boolean hasNext()
    {
        return state.i.hasNext();
    }

    /** allows remove to be called on the iterator */
    public void remove()
    {
        state.i.remove();
    }

    /** called by each iteration step */
    protected abstract void nextObject(T obj);

    protected void _return(R o)
    {
        state.return_object = o;
        state.do_break = true;
    }

    protected void _returnNotNull(R o)
    {
        if (o != null)
        {
            state.return_object = o;
            state.do_break = true;
        }
    }

    protected void _break()
    {
        state.do_break = true;
    }

    protected void setAmendedObject(R o)
    {
        state.amended_object = o;
    }

    protected R getAmendedObject()
    {
        return state.amended_object;
    }

    private IterativeDelegate<T> delegate;

    public void addDelegation(IterativeDelegate<T> delegate)
    {
        delegate.setIterativeCallback(this);

        if (this.delegate == null)
            this.delegate = delegate;
        else
            this.delegate.setNextDelegation(delegate);
    }
}


package org.intellimate.izou.sdk.frameworks.music.resources;

import org.intellimate.izou.events.EventModel;
import org.intellimate.izou.identification.Identification;
import org.intellimate.izou.sdk.frameworks.music.player.TrackInfo;
import org.intellimate.izou.sdk.resource.Resource;

import java.util.HashMap;
import java.util.Optional;

/**
 * A resource which holds a trackInfo for events, can also be obtained via the resourceManager (retruns the current)
 * @author LeanderK
 * @version 1.0
 */
public class TrackInfoResource extends Resource<HashMap<String, Object>> {
    @SuppressWarnings("SpellCheckingInspection")
    public static final String RESOURCE_ID = "izou.music.resource.trackinfo";

    /**
     * creates a new Resource.
     * This method is thread-safe.
     */
    public TrackInfoResource() {
        super(RESOURCE_ID);
    }

    /**
     * creates a new Resource.
     * This method is thread-safe.
     *
     * @param provider   the Provider of the Resource
     */
    public TrackInfoResource(Identification provider) {
        super(RESOURCE_ID, provider);
    }

    /**
     * creates a new Resource.
     * This method is thread-safe.
     *
     * @param provider   the Provider of the Resource
     * @param trackInfo  the resource
     */
    public TrackInfoResource(Identification provider, TrackInfo trackInfo) {
        super(RESOURCE_ID, provider, trackInfo.export());
    }

    /**
     * creates a new Resource.
     * This method is thread-safe.
     *
     * @param provider   the Provider of the Resource
     * @param consumer   the ID of the Consumer
     */
    public TrackInfoResource(Identification provider, Identification consumer) {
        super(RESOURCE_ID, provider, consumer);
    }

    /**
     * creates a new Resource.
     * This method is thread-safe.
     *
     * @param provider   the Provider of the Resource
     * @param trackInfo  the resource
     * @param consumer   the ID of the Consumer
     */
    public TrackInfoResource(Identification provider, TrackInfo trackInfo, Identification consumer) {
        super(RESOURCE_ID, provider, trackInfo.export(), consumer);
    }

    /**
     * gets the first TrackInfo if found in the EventModel
     * @param eventModel the EventModel
     * @return return the optional TrackInfo
     */
    public static Optional<TrackInfo> getTrackInfo(EventModel eventModel) {
        if (eventModel.getListResourceContainer().containsResourcesFromSource(RESOURCE_ID)) {
            return eventModel
                    .getListResourceContainer()
                    .provideResource(RESOURCE_ID)
                    .stream()
                    .findAny()
                    .flatMap(TrackInfo::importFromResource);
        } else {
            return Optional.empty();
        }
    }
}


package org.intellimate.izou.sdk.frameworks.music.player;

import org.intellimate.izou.resource.ResourceModel;

import java.util.HashMap;
import java.util.Optional;

/**
 * The Progress of the current track.
 * @author LeanderK
 * @version 1.0
 */
@SuppressWarnings("unused")
public class Progress {
    /**
     * if the audio has no specified beginning or end (for example a LiveStream of the audio output of the computer).
     * THIS DOES NOT MEAN THE INFORMATION IS UNAVAILABLE.
     */
    public static long NO_PROGRESS = -1;

    /*
     * the length of the track in milliseconds.
     */
    public static final String lengthDescriptor = "izou.music.progress.length";
    private final long length;
    public static final String knownPositionDescriptor = "izou.music.progress.knownPosition";
    private final long knownPosition;
    public static final String knownMillisTimeStampDescriptor = "izou.music.progress.knownMillisTimeStamp";
    private final long knownMillisTimeStamp;

    public Progress(long length, long knownPosition) {
        this.length = length;
        this.knownPosition = knownPosition;
        knownMillisTimeStamp = System.currentTimeMillis();
    }

    public Progress(long length, long knownPosition, long knownMillisTimeStamp) {
        this.length = length;
        this.knownPosition = knownPosition;
        this.knownMillisTimeStamp = knownMillisTimeStamp;
    }

    /**
     * returns the length of the Track in milliseconds
     * @return the length
     */
    public long getLength() {
        return length;
    }

    /**
     * returns the current position in milliseconds
     * @return the position
     */
    public long getPosition() {
        return knownPosition + (System.currentTimeMillis() - knownMillisTimeStamp);
    }

    /**
     * exports the progress
     * @return a new HashMap
     */
    public HashMap<String, Long> export() {
        HashMap<String, Long> data = new HashMap<>();
        data.put(lengthDescriptor, length);
        data.put(knownPositionDescriptor, knownPosition);
        data.put(knownMillisTimeStampDescriptor, knownMillisTimeStamp);
        return data;
    }

    /**
     * creates a Progress-object from the resourceModel
     * @param resourceModel the ResourceModel
     * @return the optional Progress
     */
    public static Optional<Progress> importResource(ResourceModel resourceModel) {
        Object resource = resourceModel.getResource();
        try {
            //noinspection unchecked
            HashMap<String, Long> data = (HashMap<String, Long>) resource;
            long length = data.get(lengthDescriptor);
            long knownPosition = data.get(knownPositionDescriptor);
            long knownTimestamp = data.get(knownMillisTimeStampDescriptor);
            return Optional.of(new Progress(length, knownPosition, knownTimestamp));
        } catch (Exception e) {
            return Optional.empty();
        }
    }

    @Override
    public String toString() {
        return "Progress{" +
                "length=" + (length / 60000) + ":" + (length % 60000) +
                ", knownPosition=" + (knownPosition / 60000) + ":" + (knownPosition % 60000) +
                ", knownMillisTimeStamp=" + knownMillisTimeStamp +
                '}';
    }
}


package org.firepick;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.File;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.util.Date;
import java.util.HashMap;
import java.util.Scanner;

/**
 * FireREST Java client. Fluent API for traversing JSON responses from FireREST web services.
 */
public class FireREST {
  static Logger logger = LoggerFactory.getLogger(FireREST.class);
  private int imageWidth = 800;
  private int imageHeight = 200;
  private BufferedImage imageBuffer;
  private int msTimeout = 500;
  private HashMap<URL, LIFOCache<BufferedImage>> imageMap = new HashMap<URL, LIFOCache<BufferedImage>>();

  public FireREST() {
  }

  private FireREST(FireREST firerest, int msTimeout) {
    this.msTimeout = msTimeout;
  }

  public FireREST withTimeout(int msTimeout) {
    return new FireREST(this, msTimeout);
  }

  /**
   * Return image dimensions of last retrieved image
   *
   * @return Point(width, height)
   */
  public Point getImageSize() {
    return new Point(imageWidth, imageHeight);
  }

  /**
   * Return an red image with the given text auto-sized to fit the current imageWidthximageHeight
   *
   * @param lines one or more lines of text
   * @return image
   */
  public BufferedImage errorImage(String... lines) {
    if (imageBuffer == null || imageBuffer.getWidth() != imageWidth || imageBuffer.getHeight() != imageHeight) {
      imageBuffer = new BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_INT_RGB);
    }
    Graphics2D g = (Graphics2D) imageBuffer.getGraphics();
    g.setBackground(new Color(64, 32, 32));
    g.setColor(new Color(255, 64, 64));
    g.clearRect(0, 0, imageWidth, imageHeight);
    int maxLen = 0;
    for (String line : lines) {
      if (line != null) {
        for (String innerLine : line.split("\n")) {
          maxLen = Math.max(innerLine.length(), maxLen);
        }
      }
    }
    int padding = 20;
    float sizeForWidth = 1.8f * (imageWidth - padding - padding) / maxLen; // should use TextLayout
    float sizeForHeight = (imageHeight - padding - padding) / lines.length;
    float lineHeight = Math.min(80, Math.max(12, Math.min(sizeForWidth, sizeForHeight)));
    float fontSize = 0.8f * lineHeight;
    Font font = g.getFont().deriveFont(fontSize);
    g.setFont(font);
    float y = fontSize + padding;
    for (String line : lines) {
      if (line != null) {
        g.drawString(line, padding, y);
        y += lineHeight;
      }
    }
    return imageBuffer;
  }

  /**
   * Return image from given URL.
   *
   * @return image from url or image with error text
   */
  public BufferedImage getImage(URL url) {
    String now = new Date().toString();
    if (url == null) {
      return errorImage(now, "(No image url)");
    }
    try {
      HttpURLConnection urlconn = (HttpURLConnection) url.openConnection();
      urlconn.setReadTimeout(msTimeout);
      urlconn.setConnectTimeout(msTimeout);
      urlconn.setRequestMethod("GET");
      urlconn.connect();
      BufferedImage image = ImageIO.read(urlconn.getInputStream());
      if (image == null) {
        return errorImage(now, "(Null image read)");
      }
      imageWidth = image.getWidth();
      imageHeight = image.getHeight();
      return image;
    } catch (SocketTimeoutException e) {
      logger.warn("getImage({}) => {} {}", url, e.getClass().getCanonicalName(), e.getMessage());
      return errorImage(now, msTimeout+"ms TIMEOUT");
    }  catch (Exception e) {
      logger.warn("getImage({}) => {} {}", url, e.getClass().getCanonicalName(), e.getMessage());
      return errorImage(now, "(No image)", url.toString(), e.getMessage());
    }
  }

  /**
   * Load json from given file resource.
   * This is a convenient equivalent to getJSON() with a file URL.
   *
   * @return JSONResult
   */
  public JSONResult getJSON(File file) {
    try {
      String json = new Scanner(file).useDelimiter("\\Z").next();
      return new JSONResult(json);
    }
    catch (Throwable e) {
      throw new FireRESTException(file.toString(), e);
    }
  }

  /**
   * HTTP GET json from given URL resource.
   *
   * @return JSONResult
   */
  public JSONResult getJSON(URL url) {
    try {
      logger.debug("Requesting {}", url);
      StringBuilder text = new StringBuilder();
      String line;

      HttpURLConnection urlconn = (HttpURLConnection) url.openConnection();
      urlconn.setReadTimeout(msTimeout);
      urlconn.setConnectTimeout(msTimeout);
      urlconn.setRequestMethod("GET");
      urlconn.connect();
      BufferedReader br = new BufferedReader(new InputStreamReader(urlconn.getInputStream()));
      while ((line = br.readLine()) != null) {
        text.append(line);
      }
      return new JSONResult(text.toString());
    }
    catch (Throwable e) {
      throw new FireRESTException(url.toString(), e);
    }
  }

}