package de.jformchecker;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import de.jformchecker.criteria.ValidationResult;

/**
 * Vacade for access to formchecker whitin templates
 *
 * @author jochen
 *
 */
public class View {
	FormCheckerForm form;
	GenericFormBuilder formBuilder;
	FormChecker fc;

	// for access objects
	Map<String, String> elements;
	Map<String, String> inputs;
	Map<String, String> labels;
	Map<String, String> helps;
	Map<String, Boolean> isError;
	// TODO: Error-msgs

	// RFE: try to avoid passing fc
	View(FormCheckerForm form, GenericFormBuilder formBuilder, FormChecker fc) {
		this.form = form;
		this.formBuilder = formBuilder;
		this.fc = fc;

	}

	/**
	 * the access objects should only be build, if the template engine can't call methods with params.
	 * Not capable templates that need this: mustache...
	 */
	public View generateAccessObjects() {
		elements = new HashMap<>();
		inputs = new HashMap<>();
		labels = new HashMap<>();
		helps = new HashMap<>();
		isError = new HashMap<>();

		form.elements.forEach(elem -> elements.put(elem.getName(), getElement(elem.getName())));
		form.elements.forEach(elem -> inputs.put(elem.getName(), getInput(elem.getName())));
		form.elements.forEach(elem -> labels.put(elem.getName(), getLabel(elem.getName())));
		form.elements.forEach(elem -> helps.put(elem.getName(), getHelp(elem.getName())));
		form.elements.forEach(elem -> isError.put(elem.getName(), isError(elem.getName())));
		return this;
	}

	// this method is useless in most cases. You want to build your
	// input-element via a macro!
	public String getElement(String name) {
		return formBuilder.generateHtmlForElement(fc.firstRun, fc.config.getProperties(), form.getElement(name), this.form.isHtml5Validation());
	}

	public List<String> getElementNames() {
		List<String> elementNames = new ArrayList<>();
		form.elements.forEach((elem) -> elementNames.add(elem.getName()));
		return elementNames;
	}

	public String getInput(String name) {
		return form.getElement(name).getInputTag();
	}

	public String getInput(String name, Map<String, String> map) {
		return form.getElement(name).getInputTag(map);
	}

	public String getType(String name) {
		return form.getElement(name).getType();
	}

	public boolean isError(String name) {
		return !formBuilder.getErrors(form.getElement(name), fc.firstRun).isValid();
	}

	public String getError(String name) {
		ValidationResult vr = formBuilder.getErrors(form.getElement(name), fc.firstRun);
		if (!vr.isValid()) {
			return fc.config.getProperties().getMessage(vr);
		}
		return "";
	}

	public String getLabel(String name) {
		if (!StringUtils.isEmpty(form.getElement(name).getDescription())) {
			return formBuilder.getLabelForElement(form.getElement(name), new TagAttributes(), fc.firstRun);
		} else {
			return "";
		}
	}

	public String getLabel(String name, Map<String, String> map) {
		return formBuilder.getLabelForElement(form.getElement(name), new TagAttributes(map), fc.firstRun);
	}

	public String getHelp(String name) {
		FormCheckerElement elem = form.getElement(name);
		if (!StringUtils.isEmpty(elem.getHelpText())) {
			return elem.getHelpText();
		}
		return "";
	}

	public String getStart() {
		StringBuilder formStart = new StringBuilder(formBuilder.generateFormStartTag(form, fc.formAction));
		formStart.append(formBuilder.generateCSRF(fc.req, fc.firstRun, form));
		return formStart.toString();
	}

	public String getEnd() {
		return formBuilder.getEndFormTag();
	}

	public String getForm() {
		return formBuilder.generateGenericForm(fc.formAction, fc.firstRun, form, fc.req, fc.config.getProperties());
	}

	public String getSubmit(String label) {
		String t = formBuilder.getSubmit(label);
		return t;
	}

	public String getSubmit() {
		String t = formBuilder.getSubmit(form.getSubmitLabel());
		return t;
	}

}


package com.trigonic.utils.spring.context;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import javax.servlet.ServletContext;

import org.springframework.context.ApplicationContext;
import org.springframework.web.context.ContextLoaderListener;

/**
 * This class allows linking application contexts between an outer application and inner servlet contexts hosted by an
 * embedded web server.
 *
 *

 * To use:
 *

 *
In the inner servlet context, specify a context-param in the web.xml file specifying <tt>embeddedWebContext</tt>
 * with a name for this context. Also add {@link EmbeddedWebContextConnector} as a listener.
 *
In the outer application, call {@link #registerAppContext(String, ApplicationContext)} for each embedded servlet
 * context before starting the embedded web server. The name specified should match the embeddedWebContext context-param
 * specified in the web.xml.
 *

 */
public class EmbeddedWebContextConnector extends ContextLoaderListener {
    private static final Map<String, ApplicationContext> appContextMap;

    static {
        appContextMap = new ConcurrentHashMap<String, ApplicationContext>();
    }

    /**
     * Registers the specified embedded web context.
     *
     * @return the previously-registered {@link ApplicationContext} or null if nothing previously-registered
     */
    public static ApplicationContext registerAppContext(String embeddedWebContext, ApplicationContext appContext) {
        return appContextMap.put(embeddedWebContext, appContext);
    }

    /**
     * Unregisters the specified embedded web context.
     *
     * @return the {@link ApplicationContext} that had been registered or null if nothing had been registered
     */
    public static ApplicationContext unregisterAppContext(String embeddedWebContext) {
        return appContextMap.remove(embeddedWebContext);
    }

    /**
     * Loads the parent {@link ApplicationContext} for the {@link ServletContext} by retrieving the previously
     * {@link #registerAppContext(String, ApplicationContext) registered} one for the embedded web context.
     */
    @Override
    protected ApplicationContext loadParentContext(ServletContext servletContext) {
        String name = servletContext.getInitParameter("embeddedWebContext");
        if (name == null) {
            throw new IllegalStateException("context param [embeddedWebContext] not specified for ["
                    + servletContext.getServletContextName() + "]");
        }

        ApplicationContext appContext = appContextMap.get(name);
        if (appContext == null) {
            throw new IllegalStateException("application context not registered for [" + name + "]");
        }

        return appContext;
    }
}


package de.jformchecker;

/**
 * Utils to handle ajax stuff
 * @author jochen
 *
 */
public class AjaxUtils {

	/**
	 * returns json output in this format: http://labs.omniti.com/labs/jsend
	 *
	 * { "status" : "fail", "data" : { "title" : "A title is required" } }
	 *
	 *
	 * { status : "success", data : null }
	 *
	 * @param fc
	 * @return
	 */
	public static String getJsonOutput(FormChecker fc) {
		StringBuilder json = new StringBuilder();
		String status = fc.isValidAndNotFirstRun() ? "success" : "fail";
		json.append("{\"status\": \"" + status + "\",");
		json.append("\"data\":");

		// Problems:
		if (fc.isValidAndNotFirstRun()) {
			json.append("\"null\",");
		} else {
			// append errors:
			json.append("{");
			String commaAppend = "";
			for (FormCheckerElement elem : fc.getForm().getElements()) {
				if (!elem.isValid()) {
					json.append(commaAppend);
					json.append("\"" + elem.getName() + "\":" + "\"" + elem.getValidationResult().getMessage() + "\"");
					commaAppend = ",";
				}
			}
			json.append("},");
		}

		// no problems
		json.append("\"okdata\":");
		// append errors:
		json.append("{");
		String commaAppend = "";
		for (FormCheckerElement elem : fc.getForm().getElements()) {
			if (elem.isValid()) {
				json.append(commaAppend);
				json.append("\"" + elem.getName() + "\":" + "\"" + "OK" + "\"");
				commaAppend = ",";
			}
		}
		json.append("}");

		json.append("}");

		return json.toString();
	}

}


package de.jformchecker.elements;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import de.jformchecker.FormCheckerElement;
import de.jformchecker.TagAttributes;

public class SelectInput extends AbstractInput<SelectInput> implements FormCheckerElement {

	List<SelectInputEntry> entries = new ArrayList<>();
	List<SelectInputEntryGroup> groups = new ArrayList<>();

	public static SelectInput build(String name) {
		SelectInput i = new SelectInput();
		i.name = name;
		return i;
	}

	// a map is not a good structure for that, because keys may be used more than once
	@Deprecated
	public static SelectInput build(String name, LinkedHashMap<String, String> possibleNames) {
		SelectInput si = SelectInput.build(name);
		si.setPossibleValues(possibleNames);
		return si;
	}

	// convenience method
	public static SelectInput build(String name, String keys[], String values[]) {
		SelectInput si = SelectInput.build(name);
		if (keys.length != values.length) {
			throw new IllegalArgumentException("Key / Values with unequal length");
		}
		List<SelectInputEntry> entries = new ArrayList<>();
		for (int i = 0; i < keys.length; i++) {
			entries.add(si.generateEntry(keys[i], values[i]));
		}
		si.setEntries(entries);
		return si;
	}

	// build a select-box with groups (This may be impossible to build)
	public static SelectInput build(String name, List<SelectInputEntryGroup> inputGroups) {
		SelectInput si = SelectInput.build(name);
		si.setGroups(inputGroups);
		return si;
	}

	public String getInputTag(Map<String, String> attributes) {
		TagAttributes tagAttributes = new TagAttributes(attributes);
		tagAttributes.add(this.inputAttributes);
		StringBuilder inputTag = new StringBuilder("<select " + buildAllAttributes(tagAttributes) + " name=\"" + name + "\" >\n");
		if (groups.size() > 0) {
			groups.forEach(group ->
			{
				inputTag.append("<optgroup label=\""+group.getLabel()+"\">");
				buildEntries(inputTag, group.getEntries());
				inputTag.append("</optgroup>");
			}
			);
		} else {
			buildEntries(inputTag, entries);
		}
		return inputTag.append("</select>\n").toString();
	}

	private void buildEntries(StringBuilder inputTag, List<SelectInputEntry> entries2) {
		entries2.forEach(entry->{
			String sel = "";
			if (value != null && value.equals(entry.key)) {
				sel = " SELECTED ";
			}
			inputTag.append("<option value=\"" + entry.key + "\"" + sel + ">" + entry.value + "</option>\n");
		});
	}

	@Deprecated
	public SelectInput setPossibleValues(LinkedHashMap<String, String> possibleNames) {
		List<SelectInputEntry> entries = new ArrayList<>();
		possibleNames.forEach((k,v) ->
			entries.add(this.generateEntry(k, v))
				);
		this.entries = entries;
		return this;
	}

	public SelectInput setEntries(List<SelectInputEntry> entries) {
		this.entries = entries;
		return this;
	}

	public SelectInput setGroups(List<SelectInputEntryGroup> groups) {
		this.groups = groups;
		return this;
	}

	@Override
	public void setValue(String value) {
		if (groups.isEmpty()) {
			setValueForEntryList(entries, value);
		} else {
			groups.forEach(group -> setValueForEntryList(group.getEntries(), value));
		}

	}

	private void setValueForEntryList(List<SelectInputEntry> entryList, String value) {
		for (SelectInputEntry entry : entryList) {
			if (value != null && value.equals(entry.getKey())){
				this.value = value;
				break;
			}
		}
	}

	@Override
	public String getType() {
		return "select";
	}

	public SelectInputEntry generateEntry(String key, String value) {
		return new SelectInputEntry(key, value);
	}

	// class that represents an entry in the selectInput
	public class SelectInputEntry {
		private final String key;
		private final String value;
		public SelectInputEntry(String key, String value) {
			this.key = key;
			this.value = value;
		}
		public String getKey() {
			return key;
		}
		public String getValue() {
			return value;
		}
	}

	// class that groups several inputEntries
	// See: https://www.w3schools.com/tags/tag_optgroup.asp
	public class SelectInputEntryGroup {
		private final String label;
		List<SelectInputEntry> entries;
		public SelectInputEntryGroup(String label, List<SelectInputEntry> entries) {
			this.label = label;
			this.entries = entries;
		}
		public SelectInputEntryGroup(String label) {
			this(label, new ArrayList<>());
		}
		public void addSelectInputEntry(SelectInputEntry entry) {
			entries.add(entry);
		}
		public String getLabel() {
			return label;
		}
		public List<SelectInputEntry> getEntries() {
			return entries;
		}

	}

	public SelectInputEntryGroup generateEntryGroup(String name) {
		return new SelectInputEntryGroup(name);
	}

}


package de.jformchecker.elements;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import com.coverity.security.Escape;

import de.jformchecker.AttributeUtils;
import de.jformchecker.Criterion;
import de.jformchecker.FormChecker;
import de.jformchecker.FormCheckerElement;
import de.jformchecker.StringUtils;
import de.jformchecker.TagAttributes;
import de.jformchecker.criteria.MaxLength;
import de.jformchecker.criteria.ValidationResult;
import de.jformchecker.message.MessageSource;
import de.jformchecker.request.Request;
import de.jformchecker.validator.Validator;

/**
 * Parent Element for all Formchecker elements Common stuff like validation...
 *
 * @author jochen
 *
 */
public abstract class AbstractInput <T extends FormCheckerElement> implements FormCheckerElement {

	protected String name;
	protected String value;
	protected String desc;
	protected String preSetValue = "";
	protected int size = -1;
	private List<Criterion> criteria = new ArrayList<>();
	boolean required;
	private int tabIndex;
	ValidationResult validationResult;
	protected TagAttributes inputAttributes;

	public ValidationResult getValidationResult() {
		return validationResult;
	}

	public void setValidationResult(ValidationResult validationResult) {
		this.validationResult = validationResult;
		valid = validationResult.isValid();
	}

	boolean valid = true;
	FormChecker parent;
	String helpText;

	protected String buildAllAttributes(Map<String, String> attributes) {
		return this.buildAllAttributes(new TagAttributes(attributes), t -> "", false);
	}

	protected String buildAllAttributes(TagAttributes tagAttributes) {
		return this.buildAllAttributes(tagAttributes, t -> "", false);
	}

	// builds attribs, elementId, TabIndex
	protected String buildAllAttributes(TagAttributes tagAttributes, MessageSource messageSource, boolean html5Validation) {
		StringBuilder allAttribs = new StringBuilder();
		allAttribs.append(AttributeUtils.buildAttributes(tagAttributes));
		allAttribs.append(getElementId());
		allAttribs.append(getTabIndexTag());
		if (html5Validation) {
			allAttribs.append(buildRequiredAttribute());
			allAttribs.append(buildFcRequiredMessage(messageSource));
		}
		allAttribs.append(buildSizeAttribute());
		// help-text
		if (!StringUtils.isEmpty(helpText)) {
			allAttribs.append(
					AttributeUtils.buildAttributes(new TagAttributes("aria-describedby",
							parent.getConfig().getFormBuilder().getHelpBlockId(this))));
		}
		return allAttribs.toString();
	}

	private String buildFcRequiredMessage(MessageSource messageSource) {
		String requiredMessage = messageSource.getMessage("jformchecker.required");
		if (!StringUtils.isEmpty(requiredMessage)) {
			return " data-errormessage-value-missing=\""+ requiredMessage +"\" ";
		}
		return "";
	}

	public void addCriteria(Criterion c) {
		criteria.add(c);
	}

	private Object buildSizeAttribute() {
		if (size != -1) {
			return AttributeUtils.buildSingleAttribute("size", Integer.toString(size));
		}
		return "";
	}

	protected String buildRequiredAttribute() {
		if (required) {
			return " required ";
		} else {
			return "";
		}
	}

	public String getInputTag() {
		return getInputTag(new HashMap<>(), t -> "", false);
	}

	public String getInputTag(Map<String, String> attributes, MessageSource messageSource, boolean html5Validation) {
		return getInputTag(attributes);
	}

	/**
	 * This is deprecated. Please use the getInputTag(attributes, messageSource) method
	 *
	 */
	@Deprecated
	public String getInputTag(Map<String, String> attributes) {
		return "";
	}

	// return highest tabindex of this element
	public int getLastTabIndex() {
		return tabIndex;
	}

	@Override
	public void setFormChecker(FormChecker fc) {
		parent = fc;
	}

	public String getValueHtmlEncoded() {
		return Escape.html(value);
	}

	public void setInvalid() {
		valid = false;
	}

	@Override
	public void init(Request request, boolean firstRun, Validator validator) {
		if (firstRun) {
			this.setValue(this.getPreSetValue());
		} else {
			this.setValue(request.getParameter(this.getName()));
			this.setValidationResult(validator.validate(this));
		}
	}

	public T setRequired() {
		this.required = true;
		return (T) this;
	}

	@Override
	public String getLabel() {
		Map<String, String> map = new LinkedHashMap<>();
		return parent.getLabelForElement(this, map);
	}

	@Override
	public String getPreSetValue() {
		return preSetValue;
	}

	@Override
	public T setPreSetValue(String preSetValue) {
		this.preSetValue = preSetValue;
		this.value = preSetValue;
		return (T) this;
	}

	@Override
	public String getCompleteInput() {
		return getLabel() + getInputTag();
	}

	// builds the maxlen attribute
	public String buildMaxLen() {
		List<Criterion> criteria = this.getCriteria();
		if (criteria != null) {
			for (Criterion criterion : criteria) {
				if (criterion instanceof MaxLength) {
					return AttributeUtils.buildSingleAttribute("maxlength", Integer.toString(((MaxLength) criterion).getMaxLength()));
				}
			}
		}
		return "";
	}

	@Override
	public String getName() {
		return name;
	}

	@Override
	public String getValue() {
		return value;
	}

	public void setValue(String value) {
		this.value = value;
	}

	@Override
	public T setDescription(String desc) {
		this.desc = desc;
		return (T) this;
	}

	@Override
	public void changeDescription(String desc) {
		this.desc = desc;
	}

	@Override
	public String getDescription() {
		return desc;
	}

	@Override
	public boolean isValid() {
		return valid;
	}

	public T setCriterias(Criterion... criteria) {
		this.criteria.addAll(Arrays.asList(criteria));
		return (T) this;
	}

	public boolean isRequired() {
		return required;
	}

	protected String getElementId() {
		return AttributeUtils.buildSingleAttribute("id", "form-" + name);
	}

	public int getTabIndex() {
		return tabIndex;
	}

	public String getTabIndexTag() {
		return AttributeUtils.buildSingleAttribute("tabindex", Integer.toString(getTabIndex()));
	}

	public String getTabIndexTagIncreaseBy(int addition) {
		return AttributeUtils.buildSingleAttribute("tabindex", Integer.toString(getTabIndex() + addition));
	}

	public T setTabIndex(int tabIndex) {
		this.tabIndex = tabIndex;
		return (T) this;
	}

	public List<Criterion> getCriteria() {
		return criteria;
	}

	public String getHelpText() {
		return helpText;
	}

	public T setHelpText(String helpText) {
		this.helpText = helpText;
		return (T) this;
	}

	public T setSize(int size) {
		this.size = size;
		return (T) this;
	}

	public void setName(String name) {
		this.name = name;
	}

	public void setInputAttributes(TagAttributes inputAttributes) {
		this.inputAttributes = inputAttributes;
	}

}


package de.jformchecker.elements;

import java.util.LinkedHashMap;
import java.util.Map;

import de.jformchecker.AttributeUtils;
import de.jformchecker.FormCheckerElement;

public class RadioInput extends AbstractInput<RadioInput> implements FormCheckerElement {

	LinkedHashMap<String, String> possibleNames = new LinkedHashMap<>(); // Linked
																			// Hashmap
																			// to
																			// maintain
																			// sort
																			// order

	public static RadioInput build(String name) {
		RadioInput i = new RadioInput();
		i.name = name;
		return i;
	}

	// convenience method
	public static RadioInput build(String name, LinkedHashMap<String, String> possibleNames) {
		RadioInput si = RadioInput.build(name);
		si.setPossibleValues(possibleNames);
		return si;
	}

	// convenience method
	public static RadioInput build(String name, String keys[], String values[]) {
		RadioInput si = RadioInput.build(name);
		if (keys.length != values.length) {
			throw new IllegalArgumentException("Key / Values with unequal lenght");
		}
		LinkedHashMap<String, String> possibleNames = new LinkedHashMap<>();
		for (int i = 0; i < keys.length; i++) {
			possibleNames.put(keys[i], values[i]);
		}
		si.setPossibleValues(possibleNames);
		return si;
	}

	public String getInputTag(Map<String, String> attributes) {
		StringBuilder inputTag = new StringBuilder();
		possibleNames.forEach((key, value) -> {
			// leer - bedeutet: Radio - Button ist optional, also nicht als
			// radio ausgeben!
			if (!"".equals(value)) {
				inputTag.append(this.getInputTag(key, attributes) + " <label for=\"form-radio-" + name + "-" + key + "\" class=\""
						+ "" + "\" id=\"label-" + name + "-" + key + "\">" + value + " </label>\n");
			}
			// do not increase tab-index:
			// http://stackoverflow.com/questions/14322564/can-you-tab-through-all-radio-buttons

		});
		return inputTag.toString();
	}

	public String getInputTag(String curValue, Map<String, String> attributes) {
		return "<input id=\"form-radio-" + name + "-" + curValue + "\" " + AttributeUtils.buildAttributes(attributes)
				+ getTabIndexTag() + " type=\"radio\" name=\"" + name + "\"  value=\"" + curValue + "\" "
				+ getCheckedStatus(curValue) + "" + " " + " >\n";
	}

	private String getCheckedStatus(String _name) {
		if (value != null && value.equals(_name)) {
			return "checked";
		} else {
			return "";
		}
	}

	@Override
	public int getLastTabIndex() {
		return this.getTabIndex();
	}

	public RadioInput setPossibleValues(LinkedHashMap<String, String> possibleNames) {
		this.possibleNames = possibleNames;
		return this;
	}

	@Override
	public void setValue(String value) {
		if (possibleNames.containsKey(value)) {
			this.value = value;
		}
	}

	@Override
	public String getType() {
		return "radio";
	}

}


package de.jformchecker.validator;

import de.jformchecker.Criterion;
import de.jformchecker.FormCheckerElement;
import de.jformchecker.criteria.ValidationResult;

/**
 * Validator, that checks a complete form
 *
 * @author jochen
 *
 */
public class DefaultValidator implements Validator {

	/*
	 * (non-Javadoc)
	 *
	 * @see de.jformchecker.validator.Validator#validate(de.jformchecker.
	 * FormCheckerElement)
	 */
	@Override
	public ValidationResult validate(FormCheckerElement elem) {
		String value = elem.getValue();
		ValidationResult vr = ValidationResult.ok();
		if (value != null && !"".equals(value)) {
			vr = allCriteriaSatisfied(elem);
		} else {
			// blank input is valid if it's not required
			if (elem.isRequired()) {
				vr = ValidationResult.fail("jformchecker.required", "");
			}
		}
		return vr;
	}

	// RFE: Maybe return here an array? So we can have more than one error-message per field.
	private ValidationResult allCriteriaSatisfied(FormCheckerElement elem) {
		for (Criterion criterion : elem.getCriteria()) {
			ValidationResult vr = criterion.validate(elem);
			if (!vr.isValid()) {
				return vr;
			}
		}

		return ValidationResult.ok();
	}

}


package de.jformchecker.criteria;

/**
 * Holds the result of a validation including the possible error-message
 *
 * @author jochen
 *
 */
public class ValidationResult {

	boolean isValid = false;

	// caching:
	private static ValidationResult okResult =  new ValidationResult(true, "", null, null);

	public boolean isValid() {
		return isValid;
	}

	public String getMessage() {
		return message;
	}

	public Object[] getErrorVals() {
		return errorVals;
	}

	String message;
	String translatedMessage;
	Object[] errorVals;

	public ValidationResult(boolean isValid, String message, Object[] errorVals, String translatedMessage) {
		this.isValid = isValid;
		this.message = message;
		this.errorVals = errorVals;
		this.translatedMessage = translatedMessage;
	}

	// factory methods
	public static ValidationResult of_(boolean isValid, String message, Object... errorVals) {
		return new ValidationResult(isValid, message, errorVals, null);
	}

	public static ValidationResult fail(String message, Object... errorVals) {
		return new ValidationResult(false, message, errorVals, null);
	}

	public static ValidationResult failWithTranslated(String message, Object... errorVals) {
		return new ValidationResult(false, null, errorVals, message);
	}

	public static ValidationResult failWithTranslated(String message) {
		return new ValidationResult(false, null, new Object[0], message);
	}

	public static ValidationResult ok() {
		// RFE: Could return always the same object! will reduce memory
		// footprint!
		return okResult;
	}

	public String getTranslatedMessage() {
		return translatedMessage;
	}

}


package de.jformchecker.elements;

import java.time.DateTimeException;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.Date;
import java.util.Map;
import de.jformchecker.FormCheckerElement;
import de.jformchecker.StringUtils;
import de.jformchecker.TagAttributes;
import de.jformchecker.criteria.ValidationResult;
import de.jformchecker.message.CommonSelects;
import de.jformchecker.message.MessageSource;
import de.jformchecker.request.Request;
import de.jformchecker.validator.Validator;

// DateInput Compound Element offering the date-inputs as select-boxes
// TODO: allow individual order (for internationalisation)
public class DateInputSelectCompound extends AbstractInput<DateInputSelectCompound>
    implements FormCheckerElement {

  SelectInput day;
  SelectInput month;
  SelectInput year;

  static String months[] = {"january", "february", "march", "april", "may", "june", "july",
      "august", "september", "october", "november", "december"};

  int yearStart;
  int yearEnd;

  public static DateInputSelectCompound build(String name, int yearStart, int yearEnd) {
    DateInputSelectCompound i = new DateInputSelectCompound();
    i.yearStart = yearStart;
    i.yearEnd = yearEnd;
    i.name = name;
    return i;
  }

  public static DateInputSelectCompound build(String name, YearRange yearRange) {
    return DateInputSelectCompound.build(name, yearRange.start, yearRange.end);
  }

  LocalDate internalDate = null;

  @Override
  public String getInputTag(Map<String, String> attributes) {
    TagAttributes tagAttributes = new TagAttributes(attributes);
    tagAttributes.add(this.inputAttributes);

    return day.getInputTag(tagAttributes.getAttributes())
        + month.getInputTag(tagAttributes.getAttributes())
        + year.getInputTag(tagAttributes.getAttributes());

  }

  // please use setPresetValue
  @Deprecated
  public DateInputSelectCompound presetValue(LocalDate t) {
    return setPresetValue(t);
  }

  public DateInputSelectCompound setPresetValue(LocalDate t) {
    internalDate = t;
    return this;
  }

  public static final int MONTH = 1;
  public static final int DAY = 2;
  public static final int YEAR = 3;

  public String getDatePartTag(int field, Map<String, String> attributes) {
    String inputField = "wrong field desc!";
    switch (field) {
      case DAY:
        inputField = day.getInputTag(attributes);
        break;
      case MONTH:
        inputField = month.getInputTag(attributes);
        break;
      case YEAR:
        inputField = year.getInputTag(attributes);
        break;
    }
    return inputField;
  }

  @Override
  public void init(Request request, boolean firstRun, Validator validator) {

    createSelectInputs(this.parent.getConfig().getProperties());

    if (firstRun) {
      if (internalDate != null) {
        this.setValue(this.getPreSetValue());
        day.setPreSetValue(Integer.toString(internalDate.getDayOfMonth()));
        month.setPreSetValue(Integer.toString(internalDate.getMonthValue()));
        year.setPreSetValue(Integer.toString(internalDate.getYear()));
      }

    } else {
      day.init(request, firstRun, validator);
      month.init(request, firstRun, validator);
      year.init(request, firstRun, validator);

      // if empty and not required, everything is fine
      if (!isRequired() && StringUtils.isEmpty(day.getValue())
          && StringUtils.isEmpty(month.getValue()) && StringUtils.isEmpty(year.getValue())) {
        this.valid = true;
      } else { // check date format
        String dateVal = year.getValue() + "-" + month.getValue() + "-" + day.getValue();
        this.setValue(dateVal);

        try {
          internalDate = LocalDate.of(Integer.parseInt(year.getValue()),
              Integer.parseInt(month.getValue()), Integer.parseInt(day.getValue()));
          this.valid = true;
          ValidationResult vr = validator.validate(this);
          this.setValidationResult(vr);
        } catch (DateTimeException | NumberFormatException e) {
          this.valid = false;
          this.setValidationResult(ValidationResult.fail("jformchecker.wrong_date_format"));

        }
      }
    }
  }

  private void createSelectInputs(MessageSource messageSource) {
    CommonSelects commonSelects = new CommonSelects(messageSource);
    day = SelectInput.build("day-" + name);
    day.setPossibleValues(commonSelects.buildDays());

    month = SelectInput.build("month-" + name);
    month.setPossibleValues(commonSelects.buildMonths());

    year = SelectInput.build("year-" + name);
    // iterate from large to small for years
    if (yearStart < yearEnd) {
      int temp = yearStart;
      yearStart = yearEnd;
      yearEnd = temp;
    }
    year.setPossibleValues(commonSelects.getYears(this.yearStart, this.yearEnd));
  }

  @Override
  public int getLastTabIndex() {
    return this.getTabIndex() + 2;
  }

  public String getValue() {
    return value;
  }

  public LocalDate getDateValue() {
    return internalDate;
  }

  /**
   * Get internal Date as Date (Please avoid this, because LocalDate is far better.)
   *
   * @return
   */
  public Date getLegacyDateValue() {
    if (internalDate == null) {
      return null;
    }
    return Date.from(internalDate.atStartOfDay(ZoneId.systemDefault()).toInstant());
  }

  /**
   * a range of years - that can be shown in the year-dropdown Use the static convenience methods
   *
   * @author jochen
   *
   */
  public static class YearRange {
    int start;
    int end;

    /**
     * A range of year.
     *
     * @param start The smaller value!
     * @param end The bigger value!
     */
    public YearRange(int start, int end) {
      this.start = start;
      this.end = end;
    }

    /**
     * year around now. Example: AroundNow(2) will return for the currentYear=2017 :
     * 2015,2016,2017,2018,2019
     *
     * @param offset
     * @return
     */
    public static YearRange aroundNow(int offset) {
      return new YearRange(LocalDate.now().getYear() - offset, LocalDate.now().getYear() + offset);
    }

    /**
     * year around year. Example: AroundNow(2019, 2) will return : 2017,2018,2019,2020,2021
     *
     * @param offset
     * @return
     */
    public static YearRange aroundYear(int year, int offset) {
      return new YearRange(year - offset, year + offset);
    }

    /**
     * Years in the past from today
     *
     * @param numberOfYears
     * @return
     */
    public static YearRange past(int numberOfYears) {
      return new YearRange(LocalDate.now().getYear() - numberOfYears, LocalDate.now().getYear());
    }

    /**
     * Just the current year
     *
     * @return
     */
    public static YearRange currentYear() {
      return new YearRange(LocalDate.now().getYear(), LocalDate.now().getYear());
    }

    /**
     * Years from now in the future.
     *
     * @param numberOfYears
     * @return
     */
    public static YearRange future(int numberOfYears) {
      return new YearRange(LocalDate.now().getYear(), LocalDate.now().getYear() + numberOfYears);
    }

    /**
     * typical Birthday - : 110years in the past - today
     *
     * @return
     */
    public static YearRange birthday() {
      return new YearRange(LocalDate.now().getYear() - 110, LocalDate.now().getYear());
    }

  }

  @Override
  public String getType() {
    return "date-select";
  }

}


package com.zoomulus.weaver.rest.resource;

import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpMethod;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import javax.ws.rs.Consumes;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;

import lombok.Value;
import lombok.experimental.Builder;

import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.zoomulus.weaver.core.content.ContentType;
import com.zoomulus.weaver.rest.content.HttpContent;
import com.zoomulus.weaver.rest.contenttype.ContentTypeResolverStrategy;
import com.zoomulus.weaver.rest.contenttype.IntelligentContentTypeResolverStrategy;
import com.zoomulus.weaver.rest.exceptions.StrictParamsMismatchException;
import com.zoomulus.weaver.rest.resource.ResourceArgs.ResourceArgsBuilder.ResourceArgsBuilderException;
import com.zoomulus.weaver.rest.response.ResponseFactory;

@Value
@Builder
public class Resource
{
    Class<?> referencedClass;
    Method referencedMethod;
    String path;
    HttpMethod httpMethod;
    List<String> consumesContentTypes;
    List<String> producesContentTypes;

    Map<String, String> pathParams = Maps.newHashMap();

    ContentTypeResolverStrategy inboundContentTypeResolverStrategy =
            new IntelligentContentTypeResolverStrategy();

    // TODO:
    // Handle javax.ws.rs.WebApplicationException (chap 7)
    // Support all injected parameter types:
    //  - HeaderParam
    //  - CookieParam
    //  - BeanParam
    //  - Context
    //  - Encoded
    // Support ParamConverter<T>
    // Ensure most optimal match works

    private List<ContentType> getAcceptedContentTypes()
    {
        Annotation consumesAnnotation = referencedMethod.getAnnotation(Consumes.class);
        if (null == consumesAnnotation) consumesAnnotation = referencedClass.getAnnotation(Consumes.class);
        final List<ContentType> acceptedContentTypes = Lists.newArrayList();
        if (null != consumesAnnotation)
        {
            for (final String cts : ((Consumes)consumesAnnotation).value())
            {
                try
                {
                    acceptedContentTypes.add(ContentType.valueOf(cts));
                }
                catch (IllegalArgumentException e) { }
            }
        }
        return acceptedContentTypes;
    }

    private List<ContentType> getRequestContentTypes(final Optional<HttpHeaders> headers)
    {
        return getContentTypesForHeader(headers, HttpHeaders.Names.CONTENT_TYPE);
    }

    private List<ContentType> getAcceptContentTypes(final Optional<HttpHeaders> headers)
    {
        return getContentTypesForHeader(headers, HttpHeaders.Names.ACCEPT);
    }

    private List<ContentType> getContentTypesForHeader(final Optional<HttpHeaders> headers, final String headerName)
    {
        final List<ContentType> requestContentTypes = Lists.newArrayList();
        if (headers.isPresent())
        {
            for (final String cts : headers.get().getAll(headerName))
            {
                try
                {
                    requestContentTypes.add(ContentType.valueOf(cts));
                }
                catch (IllegalArgumentException e) { }
            }
        }
        return requestContentTypes;
    }

    private List<ContentType> getProducesContentTypes(final Object response)
    {
        final List<ContentType> contentTypes = Lists.newArrayList();
        if (response instanceof Response && ((Response)response).getMediaType() != null)
        {
            contentTypes.add(new ContentType(((Response)response).getMediaType().toString()));
        }
        else
        {
            Annotation producesAnnotation = referencedMethod.getAnnotation(Produces.class);
            if (null == producesAnnotation) producesAnnotation = referencedClass.getAnnotation(Produces.class);
            if (null != producesAnnotation)
            {
                for (final String cts : ((Produces)producesAnnotation).value())
                {
                    try
                    {
                        contentTypes.add(ContentType.valueOf(cts));
                    }
                    catch (IllegalArgumentException e)
                    {
                        try
                        {
                            contentTypes.add(new ContentType(cts));
                        }
                        catch (IllegalArgumentException e2) { }
                    }
                }
            }
        }
        return contentTypes;
    }

    private Object invokeEndpoint(final String messageBody,
            final ResourcePath resourcePath,
            final Optional<HttpHeaders> headers,
            final Map<String, List<String>> queryParams)
                    throws JsonParseException, JsonMappingException, InstantiationException, IllegalAccessException,
                    IllegalArgumentException, InvocationTargetException, ResourceArgsBuilderException,
                    IOException, NoSuchMethodException, SecurityException, StrictParamsMismatchException
    {
        boolean methodRequiresPayload = (HttpMethod.PUT == httpMethod
                || HttpMethod.POST == httpMethod
                || HttpMethod.DELETE == httpMethod
                || HttpMethod.PATCH == httpMethod);
        boolean methodLacksPayload = ! methodRequiresPayload;

        final List<ContentType> acceptedInboundContentTypes = getAcceptedContentTypes();

        if (! acceptedInboundContentTypes.isEmpty() && methodLacksPayload)
        {
            return Response.status(Status.INTERNAL_SERVER_ERROR).build();
        }

        final Optional<HttpContent> content = HttpContent.create(messageBody,
                getRequestContentTypes(headers),
                acceptedInboundContentTypes);

        if (content.isPresent())
        {
            if (methodLacksPayload)
            {
                return Response.status(Status.INTERNAL_SERVER_ERROR).build();
            }
        }
        else if (! Strings.isNullOrEmpty(messageBody))
        {
            return Response.status(Status.UNSUPPORTED_MEDIA_TYPE).build();
        }

        final ResourceArgs resourceArgs = ResourceArgs.builder()
                .content(content)
                .resourcePath(resourcePath)
                .httpMethod(httpMethod)
                .httpHeaders(headers)
                .referencedMethod(referencedMethod)
                .queryParams(queryParams)
                .build();

        final Object[] args = resourceArgs.getArgs();

        if (referencedMethod.getParameters().length != args.length)
        {
            return Response.status(Status.BAD_REQUEST).build();
        }
        Object resourceObj = referencedClass.getConstructor((Class<?>[])null).newInstance((Object[])null);
        return referencedMethod.invoke(resourceObj, args);
    }

    public Response invoke(final String messageBody,
            final ResourcePath resourcePath,
            final Optional<HttpHeaders> headers,
            final Map<String, List<String>> queryParams)
    {
        try
        {
            Object response = invokeEndpoint(messageBody, resourcePath, headers, queryParams);

            if (null == response)
            {
                return Response.status(Status.NO_CONTENT).build();
            }

            final List<ContentType> acceptContentTypes = getAcceptContentTypes(headers);
            final List<ContentType> producesContentTypes = getProducesContentTypes(response);

            return new ResponseFactory().generate(response, acceptContentTypes, producesContentTypes);
        }
        catch (IOException e)
        {
            e.printStackTrace();
            return Response.status(Status.UNSUPPORTED_MEDIA_TYPE).build();
        }
        catch (StrictParamsMismatchException e)
        {
            e.printStackTrace();
            return Response.status(Status.BAD_REQUEST).build();
        }
        catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException | ResourceArgsBuilderException e)
        {
            e.printStackTrace();
            return Response.status(Status.INTERNAL_SERVER_ERROR).entity(e).build();
        }
    }

    public Optional<String> getPathParam(final String name)
    {
        return Optional.ofNullable(pathParams.get(name));
    }

    public boolean consumes(final String contentType)
    {
        return consumesContentTypes.contains(contentType);
    }

    public boolean produces(final String contentType)
    {
        return producesContentTypes.contains(contentType);
    }
}


package com.byoutline.eventcallback.internal;

import javax.inject.Provider;

/**
 * Helper class that checks if it is still the same session;
 *
 * @author Sebastian Kacprzak <sebastian.kacprzak at byoutline.com>
 */
public class SessionChecker {

    private final Provider<String> sessionIdProvider;
    public final String callbackStartSessionId;

    public SessionChecker(Provider<String> sessionIdProvider, String callbackStartSessionId) {
        this.sessionIdProvider = sessionIdProvider;
        this.callbackStartSessionId = callbackStartSessionId;
    }

    /**
     * Checks if currently we are during same session that we were during
     * callback creation.
     *
     * @return True if we are still during same session, false otherwise.
     */
    public boolean isSameSession() {
        String sessionId = sessionIdProvider.get();
        if (callbackStartSessionId == null) {
            return sessionId == null;
        }
        return callbackStartSessionId.equals(sessionId);
    }
}


package com.stanfy.enroscar.async;

import android.content.Context;

import com.stanfy.enroscar.async.content.CursorAsyncBuilder;
import com.stanfy.enroscar.async.internal.TaskAsync;

import java.util.concurrent.Callable;
import java.util.concurrent.Executor;

/**
 * A set of utility methods that provide basic implementations of {@link Async}.
 * @author Roman Mazur - Stanfy (http://stanfy.com)
 */
public final class Tools {

  private Tools() { }

  /**
   * Create an asynchronous operation that executes a provided task creating
   * an Android's {@link android.os.AsyncTask}.
   * @param task job to do
   * @param <D> result data type
   * @return implementation of {@link Async}, never {@code null}
   */
  public static <D> Async<D> async(final Callable<D> task) {
    return async(null, task);
  }

  /**
   * Create an asynchronous operation that executes a provided task creating
   * an Android's {@link android.os.AsyncTask} with a specified executor.
   * @param task job to do
   * @param executor executor used by AsyncTask
   * @param <D> result data type
   * @return implementations of {@link Async}, never {@code null}
   */
  public static <D> Async<D> async(final Executor executor, final Callable<D> task) {
    return new TaskAsync<>(task, executor);
  }

  /**
   * Create a builder for asynchronous operation that loads a {@link android.database.Cursor}
   * using Android's {@link android.content.ContentResolver}.
   * @param context context used to get a resolver instance
   * @return builder of {@code Cursor} load operation
   */
  public static CursorAsyncBuilder asyncCursor(final Context context) {
    return new CursorAsyncBuilder(context.getContentResolver());
  }

}


package com.stanfy.enroscar.async.content;

import android.content.ContentResolver;
import android.database.Cursor;

import com.stanfy.enroscar.async.Async;

import java.util.ArrayList;
import java.util.List;

/**
 * Builder for {@link com.stanfy.enroscar.async.Async} that loads a {@link Cursor}.
 * Built operation will query a content provider and register an observer for the specified uri.
 * Subscribers will receive {@link com.stanfy.enroscar.async.AsyncObserver#onResult(Object)}
 * each time the content resolver is notified about changes.
 * @author Roman Mazur - Stanfy (http://stanfy.com)
 */
public final class CursorAsyncBuilder extends BaseCursorAsyncBuilder<CursorAsyncBuilder, Cursor> {

  public CursorAsyncBuilder(final ContentResolver resolver) {
    super(resolver);
  }

  /**
   * Wrap a provided converter.
   * Returns a converter that is equivalent to
   * <pre>{@code
   *   return cursor.moveToNext()
   *       ? converter.toObject(cursor)
   *       : null;
   * }</pre>
   */
  public static <D> CursorConverter<D> singleRecordConverter(final CursorConverter<D> converter) {
    return new SingleRecordConverter<>(converter);
  }

  /**
   * Wrap a provided converter.
   * Returns a converter that creates of list of elements obtained with a provider converter.
   */
  public static <D> CursorConverter<List<D>> listConverter(final CursorConverter<D> converter) {
    return new ListConverter<>(converter);
  }

  /**
   * Convert {@code Cursor} into a POJO using a {@code CursorConverter}.
   */
  public <D> BaseCursorAsyncBuilder<? extends BaseCursorAsyncBuilder<?, D>, D> convert(
      final CursorConverter<D> converter) {
    return new ConvertedCursorAsyncBuilder<>(resolver, converter, params);
  }

  /**
   * Convert {@code Cursor} into a POJO using a {@code CursorConverter}.
   * Only the first cursor record will be converted. Provided {@code CursorConverter} should not
   * invoke any {@code move} methods on a cursor.
   */
  public <D> BaseCursorAsyncBuilder<? extends BaseCursorAsyncBuilder<?, D>, D> convertFirst(
      final CursorConverter<D> converter) {
    return convert(singleRecordConverter(converter));
  }

  /**
   * Convert {@code Cursor} into a {@code List} creating each item with
   * a provided {@code CursorConverter}.
   * Provided {@code CursorConverter} should not invoke any {@code move} methods on a cursor.
   */
  public <D> BaseCursorAsyncBuilder<? extends BaseCursorAsyncBuilder<?, List<D>>, List<D>>
      convertList(final CursorConverter<D> converter) {
    return convert(listConverter(converter));
  }

  @Override
  public Async<Cursor> get() {
    return new CursorAsync(new ContentProviderQuery(resolver, params), executor);
  }

  /** Converts a single record cursor to an object. */
  private static class SingleRecordConverter<D> implements CursorConverter<D> {
    private final CursorConverter<D> delegate;

    SingleRecordConverter(CursorConverter<D> delegate) {
      this.delegate = delegate;
    }

    @Override
    public D toObject(Cursor cursor) {
      return cursor.moveToNext()
          ? delegate.toObject(cursor)
          : null;
    }
  }

  /** Converts a cursor to a list. */
  private static class ListConverter<D> implements CursorConverter<List<D>> {
    private final CursorConverter<D> delegate;

    ListConverter(final CursorConverter<D> delegate) {
      this.delegate = delegate;
    }

    @Override
    public List<D> toObject(final Cursor cursor) {
      int count = cursor.getCount();
      ArrayList<D> result = new ArrayList<>(count);
      if (count == 0) {
        return result;
      }

      while (cursor.moveToNext()) {
        result.add(delegate.toObject(cursor));
      }

      return result;
    }
  }

  /** Converted cursor builder. */
  static final class ConvertedCursorAsyncBuilder<D>
      extends BaseCursorAsyncBuilder<ConvertedCursorAsyncBuilder<D>, D> {

    private final CursorConverter<D> converter;

    public ConvertedCursorAsyncBuilder(final ContentResolver resolver,
                                       final CursorConverter<D> converter,
                                       final Params params) {
      super(resolver, params);
      this.converter = converter;
    }

    @Override
    public Async<D> get() {
      if (converter == null) {
        throw new IllegalStateException("Converter is not defined");
      }
      return new ConvertedCursorAsync<>(params, converter, resolver, executor);
    }

  }

}


package com.stanfy.enroscar.async.internal;

import android.support.v4.app.LoaderManager;
import android.support.v4.content.Loader;
import android.util.SparseArray;

import com.stanfy.enroscar.async.Async;
import com.stanfy.enroscar.async.AsyncObserver;
import com.stanfy.enroscar.async.internal.OperatorBase.OperatorContext;
import com.stanfy.enroscar.async.internal.WrapAsyncLoader.Result;

/**
 * Describes what actions should be taken when some operation completes.
 * @author Roman Mazur - Stanfy (http://stanfy.com)
 */
public abstract class LoaderDescription<T extends LoaderDescription<?>> {

  /** Stub provider used to init already started loaders. */
  private static final AsyncProvider<?> STUB_PROVIDER = new AsyncProvider<Object>() {
    @Override
    public Async<Object> provideAsync() {
      throw new IllegalStateException("Loader started without providing an Async");
    }
  };

  /** Observers mapping: loader id <-> associated structure (observer, start action, options). */
  private final SparseArray<LoaderCookies> listenersMap = new SparseArray<>();

  private final OperatorContext<?> operatorContext;

  protected LoaderDescription(final OperatorContext<?> operatorContext) {
    this.operatorContext = operatorContext;
  }

  @SuppressWarnings("unchecked")
  public final T alsoWhen() {
    return (T) this;
  }

  <D> void addObserver(final int loaderId, final AsyncObserver<D> observer,
                       final boolean destroyOnFinish) {
    LoaderCookies listener = getListeners(loaderId);
    listener.observer = observer;
    if (destroyOnFinish) {
      listener.options |= LoaderCookies.DESTROY_ON_FINISH;
    } else {
      listener.options &= ~LoaderCookies.DESTROY_ON_FINISH;
    }
  }

  protected void addStartAction(final int loaderId, final Runnable action) {
    getListeners(loaderId).startAction = action;
  }

  <D> LoaderManager.LoaderCallbacks<Result<D>> makeCallbacks(final int loaderId,
                                                             final AsyncProvider<D> provider,
                                                             final boolean destroyOnFinish) {
    return new ObserverCallbacks<>(provider, operatorContext, this, loaderId, destroyOnFinish);
  }

  @SuppressWarnings("unchecked")
  <D> AsyncObserver<D> getObserver(final int id) {
    return (AsyncObserver<D>) getListeners(id).observer;
  }

  void invokeStartAction(final int id) {
    LoaderCookies cookies = getListeners(id);
    cookies.options |= LoaderCookies.ALREADY_INIT;
    if (cookies.startAction != null) {
      cookies.startAction.run();
    }
  }

  /**
   * Initialize already started loaders.
   */
  void initStartedLoaders() {
    LoaderManager lm = operatorContext.loaderManager;
    SparseArray<LoaderCookies> listenersMap = this.listenersMap;

    int count = listenersMap.size();
    for (int i = 0; i < count; i++) {
      int loaderId = listenersMap.keyAt(i);
      Loader<?> loader = lm.getLoader(loaderId);
      if (loader != null && loader.isStarted()) {
        LoaderCookies cookies = listenersMap.valueAt(i);
        boolean notInit = (cookies.options & LoaderCookies.ALREADY_INIT) == 0;
        if (notInit) {
          Utils.initLoader(
              operatorContext,
              loaderId,
              STUB_PROVIDER,
              (cookies.options & LoaderCookies.DESTROY_ON_FINISH) == LoaderCookies.DESTROY_ON_FINISH,
              this
          );
        }
      }
    }
  }

  private LoaderCookies getListeners(final int id) {
    LoaderCookies l = listenersMap.get(id);
    if (l == null) {
      l = new LoaderCookies();
      listenersMap.put(id, l);
    }
    return l;
  }

  /**
   * Structure associated with a loader.
   */
  private static final class LoaderCookies {

    static final int DESTROY_ON_FINISH = 1;
    static final int ALREADY_INIT = 2;

    /** Async observer. Listens for finish/reset events. */
    AsyncObserver<?> observer;
    /** Action to invoke on start event. */
    Runnable startAction;
    /** Bit set of options. */
    int options;
  }

}


package com.byoutline.eventcallback;

import com.byoutline.eventcallback.internal.EventPoster;
import com.byoutline.eventcallback.internal.RetrofitErrorConverter;
import com.byoutline.eventcallback.internal.SessionChecker;
import com.byoutline.eventcallback.internal.actions.CreateEvents;
import com.byoutline.eventcallback.internal.actions.ResultEvents;
import com.byoutline.eventcallback.internal.actions.ScheduledActions;
import com.google.gson.reflect.TypeToken;
import org.apache.commons.lang3.Validate;
import retrofit.Callback;
import retrofit.RetrofitError;
import retrofit.client.Response;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 *
Callback that can be safely called from fragments and activities.

 *

 * Can be configured to execute actions during following steps:
 *

 *
 onSuccess (when call was successful)

 *
 onError (when call failed)

 *
 onCreation (when this event is created)

 *

 *

 * Supported actions:
 *

 *
 posting events to {@link IBus}

 *
 posting {@link ResponseEvent}s that have server response set (available
 * only during onSuccess and onError steps since there is nothing to return on
 * creation)

 *
 setting {@link AtomicBoolean} to requested value

 *
 executing different code from {@link SuccessHandler} for all calls that
 * return matched class

 *

 * Create instance by calling
 * {@link #builder(com.byoutline.eventcallback.CallbackConfig, com.google.gson.reflect.TypeToken)}.
 *
 * @param <S> Type of response returned by server on success.
 * @param <E> Type of response returned by server on error.
 * @author Sebastian Kacprzak <sebastian.kacprzak at byoutline.com> on 17.06.14.
 */
public class EventCallback<S, E> implements Callback<S> {

    private final CallbackConfig config;
    private final TypeToken<E> validationErrorTypeToken;
    private final SessionChecker sessionChecker;

    private final ScheduledActions<CreateEvents> onCreateActions;
    private final ScheduledActions<ResultEvents<S>> onSuccessActions;
    private final ScheduledActions<ResultEvents<E>> onErrorActions;
    private final Map<Integer, ScheduledActions<CreateEvents>> onStatusCodeActions;

    private final EventPoster postHelper;

    /**
     * Creates instance. For convenience use
     * {@link #builder(CallbackConfig, TypeToken)}
     * instead of calling directly.
     */
    EventCallback(@Nonnull CallbackConfig config, @Nullable TypeToken<E> validationErrorTypeToken,
                  @Nullable String currentSessionId,
                  @Nonnull ScheduledActions<CreateEvents> onCreateActions, @Nonnull ScheduledActions<ResultEvents<S>> onSuccessActions,
                  @Nonnull ScheduledActions<ResultEvents<E>> onErrorActions, @Nonnull Map<Integer, ScheduledActions<CreateEvents>> onStatusCodeActions) {
        this.config = config;

        this.validationErrorTypeToken = validationErrorTypeToken;
        this.sessionChecker = new SessionChecker(config.sessionIdProvider, currentSessionId);
        this.onCreateActions = onCreateActions;
        this.onSuccessActions = onSuccessActions;
        this.onErrorActions = onErrorActions;
        this.onStatusCodeActions = onStatusCodeActions;

        if (config.debug) {
            validateArgs();
        }

        postHelper = new EventPoster(config.bus);
        postHelper.executeCommonActions(onCreateActions, sessionChecker.isSameSession());
    }

    private void validateArgs() {
        Validate.notNull(validationErrorTypeToken);
        Validate.notNull(onCreateActions);
        Validate.notNull(onSuccessActions);
        Validate.notNull(onErrorActions);
        Validate.notNull(onStatusCodeActions);
        onCreateActions.validate();
        onSuccessActions.validate();
        onErrorActions.validate();
        for (ScheduledActions<CreateEvents> action : onStatusCodeActions.values()) {
            action.validate();
        }
    }

    /**
     * Returns builder that creates {@link EventCallback}.
     *

     * To avoid passing all arguments on each callback creation it is suggested
     * to wrap this call in project. For example:
     *

     *

     * class MyEventCallback<S> {
     *

     *     CallbackConfig config = injected;
     *     private EventCallbackBuilder<S, MyHandledErrorMsg> builder() {
     *         return EventCallback.builder(config, new TypeToken<MyHandledErrorMsg>(){});
     *     }
     *

     *     public static <S> EventCallbackBuilder<S, MyHandledErrorMsg> ofType() {
     *         return new MyEventCallback<S>().builder(responseType);
     *     }
     * }
     *

     *
     * @param <S>            Type of response returned by server onSuccess
     * @param <E>            Type of response returned by server onError
     * @param config         Shared configuration
     * @param errorTypeToken TypeToken that provides information about expected
     *                       response returned by server
     * @return Builder that assists in creating valid EventCallback in readable
     * way.
     */
    public static <S, E> EventCallbackBuilder<S, E> builder(@Nonnull CallbackConfig config,
                                                            @Nonnull TypeToken<E> errorTypeToken) {
        return new EventCallbackBuilder<S, E>(config, errorTypeToken);
    }

    @Override
    public void success(S result, Response response) {
        boolean postNullResponse = true;
        informSharedSuccessHandlers(result);
        informStatusCodeListener(response);
        postHelper.executeResponseActions(onSuccessActions, result, response, sessionChecker.isSameSession(), postNullResponse);
    }

    @Override
    public void failure(RetrofitError error) {
        boolean postNullResponse = false;
        E convertedError = RetrofitErrorConverter.<E>getAsClassOrNull(validationErrorTypeToken, error);
        informStatusCodeListener(error.getResponse());
        postHelper.executeResponseActions(onErrorActions, convertedError, error.getResponse(), sessionChecker.isSameSession(), postNullResponse);
    }

    /**
     * Passes result to {@link SuccessHandler}s from {@link Callback}
     *
     * @param result response from the server that should be passed to listeners
     */
    private void informSharedSuccessHandlers(S result) {
        if (result == null) {
            // no handler matches undefined result class
            return;
        }
        for (Map.Entry<Class, SuccessHandler> handler : config.sharedSuccessHandlers.entrySet()) {
            if (handler.getKey().isAssignableFrom(result.getClass())) {
                handler.getValue().onCallSuccess(result);
            }
        }
    }

    /**
     * Checks if any actions is associated with response status code, and
     * execute it.
     *
     * @param response response from the server that can be checked for status
     *                 code.
     */
    private void informStatusCodeListener(@Nullable Response response) {
        if (response == null) {
            return;
        }
        ScheduledActions<CreateEvents> actions = onStatusCodeActions.get(response.getStatus());
        if (actions != null) {
            postHelper.executeCommonActions(actions, sessionChecker.isSameSession());
        }
    }

    @Override
    public String toString() {
        return "EventCallback{" + "config=" + config + ",\n"
                + "validationErrorTypeToken=" + validationErrorTypeToken + ",\n"
                + "callbackStartSessionId=" + sessionChecker.callbackStartSessionId + ",\n"
                + "onCreateActions=" + onCreateActions + ",\n"
                + "onSuccessActions=" + onSuccessActions + ",\n"
                + "onErrorActions=" + onErrorActions + '}';
    }
}


/*
 * Copyright 2014 Jacopo Aliprandi, Dario Archetti
 *
 * This file is part of SPF.
 *
 * SPF is free software: you can redistribute it and/or modify it under the
 * terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option)
 * any later version.
 *
 * SPF is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
 * more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with SPF.  If not, see <http://www.gnu.org/licenses/>.
 *
 */
package it.polimi.spf.lib.profile;

import android.content.Context;
import it.polimi.spf.lib.SPF;
import it.polimi.spf.lib.SPFPerson;
import it.polimi.spf.shared.model.ProfileField;
import it.polimi.spf.shared.model.ProfileFieldContainer;
import it.polimi.spf.shared.model.SPFError;

/**
 * A {@link SPFComponent} that provides access to profiles of remote people to
 * allow read operation.
 *
 * @author darioarchetti
 *
 */
public class SPFRemoteProfile {

	private SPF mInterface;

	public SPFRemoteProfile(Context context, SPF iface) {
		this.mInterface = iface;
	}

	/**
	 * Provides a reference to a remote profile. With this reference it is
	 * possible to perform read operations and retrieve information from remote
	 * people.
	 *
	 * @param p
	 *            - the {@link SPFPerson} whose profile to load.
	 * @return an instance of {@link RemoteProfile} to interact with the remote
	 *         profile.
	 */
	public RemoteProfile getProfileOf(SPFPerson p) {
		if (p == null) {
			throw new NullPointerException();
		}

		return new RemoteProfile(p, mInterface);
	}

	protected void recycle() {
		mInterface = null;
	}

	/**
	 * Reference to the profile of a remote person that allows read operations to
	 * obtain information.
	 *
	 * @author darioarchetti
	 *
	 */
	public class RemoteProfile {

		private SPF mInterface;
		private String mPersonIndentifier;

		/* package */public RemoteProfile(SPFPerson p, SPF iface) {
			this.mInterface = iface;
			this.mPersonIndentifier = p.getIdentifier();
		}

		/**
		 * Obtains a bulk of profile fields from a remote profile. The bulk is
		 * retrieved as an instance of {@link ProfileFieldContainer} from which the
		 * single field can be retrieved.
		 *
		 * @param fields
		 *            - the list of {@link ProfileField} to retrieve from the remote
		 *            profile
		 * @return a container that holds the values of requested fields
		 */
		public ProfileFieldContainer getProfileBulk(ProfileField<?>... fields) {
			String[] fieldIdentifiers = new String[fields.length];
			for (int i = 0; i < fields.length; i++) {
				fieldIdentifiers[i] = fields[i].getIdentifier();
			}
			SPFError err =  new SPFError();
			return mInterface.getProfileBulk(mPersonIndentifier, fieldIdentifiers, err);
		}
	}

}


/**
 * The MIT License
 *
 * Copyright (C) 2015 Asterios Raptis
 *
 * 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 de.alpharogroup.xsl.transform;

import java.io.File;
import java.io.OutputStream;

import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

import lombok.NonNull;
import lombok.experimental.UtilityClass;

/**
 * The class {@link XsltTransformerExtensions}
 */
@UtilityClass
public final class XsltTransformerExtensions
{

	/** The Constant TRANSFORMER_FACTORY. */
	private static final TransformerFactory TRANSFORMER_FACTORY = TransformerFactory.newInstance();

	/**
	 * Gets a new instance from a {@link Transformer} object
	 *
	 * @param xsltFile
	 *            the xslt file
	 * @return the new {@link Transformer} object
	 * @throws TransformerConfigurationException
	 *             is thrown if there are errors when parsing the Source or it is not
	 *             possible to create a Transformer instance.
	 */
	public static Transformer getTransformer(final File xsltFile)
		throws TransformerConfigurationException
	{
		return getTransformer(new StreamSource(xsltFile));
	}

	/**
	 * Gets a new instance from a {@link Transformer} object
	 *
	 * @param xsltSource
	 *            the xslt source
	 * @return the new {@link Transformer} object
	 * @throws TransformerConfigurationException
	 *             is thrown if there are errors when parsing the Source or it is not
	 *             possible to create a Transformer instance.
	 */
	public static Transformer getTransformer(final Source xsltSource)
		throws TransformerConfigurationException
	{
		return TRANSFORMER_FACTORY.newTransformer(xsltSource);
	}

	/**
	 * Gets a new instance from a {@link Transformer} object
	 *
	 * @param xsltInputFile
	 *            the xslt input file
	 * @return the new {@link Transformer} object
	 * @throws TransformerConfigurationException
	 *             is thrown if there are errors when parsing the Source or it is not
	 *             possible to create a Transformer instance.
	 */
	public static Transformer getTransformer(final String xsltInputFile)
		throws TransformerConfigurationException
	{
		return getTransformer(new File(xsltInputFile));
	}

	/**
	 * Transform.
	 *
	 * @param xmlFile
	 *            the xml file
	 * @param xsltFile
	 *            the xslt file
	 * @param outputStream
	 *            the output stream
	 * @throws TransformerConfigurationException
	 *             is thrown if there are errors when parsing the Source or it is not
	 *             possible to create a Transformer instance.
	 * @throws TransformerException
	 *             is thrown if an unrecoverable error occurs during the course of the
	 *             transformation.
	 */
	public static void transform(final @NonNull File xmlFile, final @NonNull File xsltFile,
		final OutputStream outputStream)
		throws TransformerConfigurationException, TransformerException
	{
		final Source xmlSource = new StreamSource(xmlFile);
		final Source xsltSource = new StreamSource(xsltFile);
		transform(xmlSource, xsltSource, outputStream);
	}

	/**
	 * Transform.
	 *
	 * @param xmlSource
	 *            the xml source
	 * @param xsltSource
	 *            the xslt source
	 * @param outputStream
	 *            the output stream
	 * @throws TransformerConfigurationException
	 *             is thrown if there are errors when parsing the Source or it is not
	 *             possible to create a Transformer instance.
	 * @throws TransformerException
	 *             is thrown if an unrecoverable error occurs during the course of the
	 *             transformation.
	 */
	public static void transform(final Source xmlSource, final Source xsltSource,
		final OutputStream outputStream)
		throws TransformerConfigurationException, TransformerException
	{
		final Transformer transformer = getTransformer(xsltSource);
		transformer.transform(xmlSource, new StreamResult(outputStream));
	}

	/**
	 * Transform.
	 *
	 * @param xmlInputFile
	 *            the xml input file
	 * @param xsltInputFile
	 *            the xslt input file
	 * @param outputStream
	 *            the output stream
	 * @throws TransformerConfigurationException
	 *             is thrown if there are errors when parsing the Source or it is not
	 *             possible to create a Transformer instance.
	 * @throws TransformerException
	 *             is thrown if an unrecoverable error occurs during the course of the
	 *             transformation.
	 */
	public static void transform(final String xmlInputFile, final String xsltInputFile,
		final OutputStream outputStream)
		throws TransformerConfigurationException, TransformerException
	{
		final File xmlFile = new File(xmlInputFile);
		final File xsltFile = new File(xsltInputFile);
		transform(xmlFile, xsltFile, outputStream);
	}

}


/*
 * Copyright 2014 Jacopo Aliprandi, Dario Archetti
 * Copyright 2015 Stefano Cappa
 *
 * This file is part of SPF.
 *
 * SPF is free software: you can redistribute it and/or modify it under the
 * terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option)
 * any later version.
 *
 * SPF is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
 * more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with SPF.  If not, see <http://www.gnu.org/licenses/>.
 *
 */
package it.polimi.spf.app.view;

import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.EditText;
import android.widget.RelativeLayout;

import com.mikepenz.iconics.view.IconicsCompatButton;

import java.util.List;

import it.polimi.spf.app.R;

/**
 * @author Jacopo
 */
public class TagsPicker extends RelativeLayout {

    private TagsViewer tv;
    private EditText et;
    private IconicsCompatButton btn;
    private OnChangeListener changeListener;

    /**
     * @param context
     */
    public TagsPicker(Context context) {
        super(context);
        init();
    }

    /**
     * @param context
     * @param attrs
     */
    public TagsPicker(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    /**
     * @param context
     * @param attrs
     * @param defStyle
     */
    public TagsPicker(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init();
    }

    private void init() {
        inflate(getContext(), R.layout.view_tags_picker, this);
        tv = ((TagsViewer) findViewById(R.id.tags_viewer));
        btn = (IconicsCompatButton) findViewById(R.id.add_tag_button);
        et = (EditText) findViewById(R.id.edit_tag);
        btn.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                String text = et.getText().toString();
                if (text != null) {
                    text = text.trim();
                    if (!text.equals("")) {
                        tv.addTag(text);
                        et.setText("");
                        notifyChangeListener();
                    }
                }
            }
        });

    }

    public void setEditable(boolean editable) {
        int visibility = editable ? View.VISIBLE : View.GONE;
        et.setVisibility(visibility);
        btn.setVisibility(visibility);
        tv.setEditable(editable);
    }

    private void notifyChangeListener() {
        if (changeListener != null) {
            final OnChangeListener listener = changeListener;
            post(new Runnable() {

                @Override
                public void run() {
                    listener.onChange(getTags());
                }
            });
        }
    }

    /**
     * Set the initial list of tags. Register a OnChangeListener to be notified about changes
     *
     * @param list
     */
    public void setInitialTags(List<String> list) {
        tv.setTags(list);
    }

    /**
     * Return the list of tags.
     *
     * @return
     */
    public List<String> getTags() {
        return tv.getTags();
    }

    /**
     * OnChangeListenr is the container object of the callbacks that notifies
     * about changes in the list of tags.
     *
     * @author Jacopo
     */
    public interface OnChangeListener {
        /**
         * @param tags - the updated list of tags
         */
        void onChange(List<String> tags);
    }

    /* package */OnChangeListener getChangeListener() {
        return changeListener;
    }

    /**
     * Set the change listener.
     *
     * @param listener
     */
    public void setChangeListener(OnChangeListener listener) {
        changeListener = listener;
        if (changeListener != null) {
            // add a listener to TagViewer for removed tags events
            tv.setOnRemovedTagListener(new TagsViewer.OnRemovedListener() {
                @Override
                public void onRemovedTag(String tag) {
                    notifyChangeListener();
                }
            });
        }
    }

}


/*
 * Copyright 2014 Jacopo Aliprandi, Dario Archetti
 *
 * This file is part of SPF.
 *
 * SPF is free software: you can redistribute it and/or modify it under the
 * terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option)
 * any later version.
 *
 * SPF is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
 * more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with SPF.  If not, see <http://www.gnu.org/licenses/>.
 *
 */
package it.polimi.spf.shared.model;

import android.os.Parcel;
import android.os.Parcelable;

/**
 * TODO #Documentation
 *
 */
public class SPFTrigger implements Parcelable {

	private static final long MIN_SLEEP_PERIOD = 30/*seconds*/*1000/*millis*/;

	/*
	 * the identifier generated in SPF database
	 */
	private long id = -1;

	/*
	 * the name assigned to this SPFTrigger
	 */
	private String name = "";

	/*
	 * the query associated with the trigger
	 */
	private SPFQuery query;

	/*
	 * the action associated with this trigger
	 */
	private SPFAction action;

	/*
	 * specifies if the trigger is to be deleted after the first positive query
	 * match.
	 */
	private boolean isOneShot = true;

	/*
	 * specifies for how much the trigger is to be turned off after have been
	 * fired on a given target. this property does not affect the notification
	 * regarding different instances: e.g. entities with different identifiers.
	 * If the trigger isOneShot this property is ignored.
	 */
	private long sleepPeriod = 0;

	// not to parcel properties:

	/*
	 * Use this variable to set the status of the trigger: a trigger can be edit
	 * within beginedit and endEdit. endEdit validates the trigger.
	 *
	 * NDR this solution avoids the overhead of a builder
	 */
	private boolean editing = false;

	/**
	 * Creates a new trigger.
	 *
	 * @param name
	 *            - the name of the trigger
	 * @param query
	 *            - the query of the trigger
	 * @param action
	 *            - the action to be executed
	 * @param oneShot
	 *            - if the trigger is oneShot
	 * @param sleepPeriod
	 *            - the sleep period of this trigger
	 *
	 * @throws IllegalTriggerException
	 *             - if the trigger is not valid
	 */
	@Deprecated
	public SPFTrigger(String name, SPFQuery query, SPFAction action, boolean oneShot, long sleepPeriod) throws IllegalTriggerException {
		this(-1, name, query, action, oneShot, sleepPeriod);
	}

	/**
	 * Creates a new trigger with a specified sleep period.
	 *
	 * @param name
	 *            - the name of the trigger
	 * @param query
	 *            - the query of the trigger
	 * @param action
	 *            - the action to be executed
	 * @param oneShot
	 *            - if the trigger is oneShot
	 * @param sleepPeriod
	 *            - the sleep period of this trigger
	 *
	 * @throws IllegalTriggerException
	 *             - if the trigger is not valid
	 */
	public SPFTrigger(String name, SPFQuery query, SPFAction action, long sleepPeriod) throws IllegalTriggerException {
		this(-1, name, query, action, false, sleepPeriod);
	}

	/**
	 * Creates a new one-shot trigger.
	 *
	 * @param name
	 *            - the name of the trigger
	 * @param query
	 *            - the query of the trigger
	 * @param action
	 *            - the action to be executed
	 *
	 * @throws IllegalTriggerException
	 *             - if the trigger is not valid
	 */
	public SPFTrigger(String name, SPFQuery query, SPFAction action) throws IllegalTriggerException {
		this(-1, name, query, action, true, 0);
	}

	//TODO hide internal constructor
	/**
	 * INTERNAL USE
	 * Creates a new trigger.
	 *
	 * @param id
	 *            - the id of the trigger
	 * @param name
	 *            - the name of the trigger
	 * @param query
	 *            - the query of the trigger
	 * @param action
	 *            - the action to be executed
	 * @param oneShot
	 *            - if the trigger is oneShot
	 * @param sleepPeriod
	 *            - the sleep period of this trigger
	 *
	 * @throws IllegalTriggerException
	 *             - if the trigger is not valid
	 */
	public SPFTrigger(long id, String name, SPFQuery query, SPFAction action, boolean oneShot, long activePeriod) throws IllegalTriggerException {
		this.id = id;
		this.name = name;
		this.query = query;
		this.action = action;
		this.isOneShot = oneShot;
		if (this.isOneShot) {
			sleepPeriod = Long.MAX_VALUE;
		} else {
			sleepPeriod = sleepPeriod>=MIN_SLEEP_PERIOD? sleepPeriod:MIN_SLEEP_PERIOD;
			this.sleepPeriod = activePeriod;
		}
	}

	/**
	 * The identifier of this trigger, it is set to -1 if it is volatile (not
	 * saved).
	 *
	 * @return the id
	 */
	public long getId() {
		return id;
	}

	/**
	 * Set the id of the trigger. It is the primary key to use when accessing
	 * the triggers' table.
	 *
	 * @param id
	 *            the id to set
	 */
	public void setId(long id) {
		this.id = id;
	}

	/**
	 * Enters the editable state.
	 */
	public void beginEdit() {
		this.editing = true;
	}

	/**
	 * Exits from the editable state and validates the content.
	 *
	 * @throws IllegalTriggerException
	 */
	public void endEdit() throws IllegalTriggerException {
		this.editing = false;
		validate();
	}

	private void validate() throws IllegalTriggerException {
		// TODO implement validation on query, action and other parameters
	}

	/*
	 * check if the instance is in the 'editable' state. If it is not, throws a
	 * runtime exception: TriggerNotEditableException
	 */
	private void checkIfEditable() {
		if (!this.editing) {
			throw new TriggerNotEditableException("Attempt to modify a trigger not in editable state");
		}
	}

	/**
	 * @return the name
	 */
	public String getName() {
		return name;
	}

	/**
	 * @param name
	 *            the name to set
	 */
	public void setName(String name) {
		checkIfEditable();
		this.name = name;
	}

	/**
	 * @return the isOneShot
	 */
	public boolean isOneShot() {
		return isOneShot;
	}

	/**
	 * @param isOneShot
	 *            the isOneShot to set
	 */
	public void setOneShot(boolean isOneShot) {
		checkIfEditable();
		this.isOneShot = isOneShot;
		if (this.isOneShot) {
			this.sleepPeriod = Long.MAX_VALUE;
		}
	}

	/**
	 * Returns the sleepPeriod of this trigger.
	 *
	 * @return the sleepPeriod
	 */
	public long getSleepPeriod() {
		return sleepPeriod;
	}

	/**
	 * @param sleepPeriod
	 *            the sleepPeriod to set
	 */
	public void setSleepPeriod(long sleepPeriod) {
		checkIfEditable();
		this.sleepPeriod = sleepPeriod;
		this.isOneShot = false;
	}

	/**
	 * @return the action
	 */
	public SPFAction getAction() {
		return action;
	}

	/**
	 * @param sPFAction
	 *            the action to set
	 */
	public void setAction(SPFAction sPFAction) {
		checkIfEditable();
		this.action = sPFAction;
	}

	/**
	 * @return the query
	 */
	public SPFQuery getQuery() {
		return query;
	}

	/**
	 * @param query
	 *            the query to set
	 */
	public void setQuery(SPFQuery query) {
		checkIfEditable();
		this.query = query;
	}

	/**
	 * Exception thrown when the trigger state is inconsistent.
	 *
	 */
	public class IllegalTriggerException extends Exception {

		private static final long serialVersionUID = 1L;

	}

	/**
	 * Exception thrown when there is an attempt to modify the trigger while it
	 * is not in the editable state.
	 *
	 */
	public class TriggerNotEditableException extends RuntimeException {

		public TriggerNotEditableException(String message) {
			super(message);
		}

		private static final long serialVersionUID = 1L;

	}

	/*
	 * Parcelable interface methods
	 */

	/*
	 * (non-Javadoc)
	 *
	 * @see android.os.Parcelable#describeContents()
	 */
	@Override
	public int describeContents() {
		return 0;
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see android.os.Parcelable#writeToParcel(android.os.Parcel, int)
	 */
	@Override
	public void writeToParcel(Parcel dest, int flags) {
		dest.writeParcelable(query, flags);
		dest.writeParcelable(action, flags);
		dest.writeLong(id);
		dest.writeInt(isOneShot ? 1 : 0);
		dest.writeLong(sleepPeriod);
		dest.writeString(name);
	}

	private SPFTrigger(Parcel source) {
		this.query = source.readParcelable(((Object) this).getClass().getClassLoader());
		this.action = source.readParcelable(((Object) this).getClass().getClassLoader());
		this.id = source.readLong();
		this.isOneShot = (source.readInt() == 1) ? true : false;
		this.sleepPeriod = source.readLong();
		this.name = source.readString();
	}

	public final static Creator<SPFTrigger> CREATOR = new Creator<SPFTrigger>() {

		@Override
		public SPFTrigger[] newArray(int size) {
			return new SPFTrigger[size];
		}

		@Override
		public SPFTrigger createFromParcel(Parcel source) {
			return new SPFTrigger(source);
		}
	};

}


/**
 * The MIT License
 *
 * Copyright (C) 2015 Asterios Raptis
 *
 * 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 de.alpharogroup.xsd.schema;

import java.io.File;
import java.io.IOException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.dom.DOMSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;

import org.w3c.dom.Document;
import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXException;

import lombok.experimental.UtilityClass;

/**
 * The class {@link ValidatorExtensions} can validate xml files.
 */
@UtilityClass
public final class ValidatorExtensions
{

	/** The Constant DOCUMENT_BUILDER_FACTORY_VALUE. */
	private static final String DOCUMENT_BUILDER_FACTORY_VALUE = "org.apache.xerces.jaxp.DocumentBuilderFactoryImpl";

	/** The Constant DOCUMENT_BUILDER_FACTORY_KEY. */
	private static final String DOCUMENT_BUILDER_FACTORY_KEY = "javax.xml.parsers.DocumentBuilderFactory";

	/** The Constant SCHEMA_SOURCE_KEY. */
	private static final String SCHEMA_SOURCE_KEY = "http://java.sun.com/xml/jaxp/properties/schemaSource";

	/** The Constant SCHEMA_LANGUAGE_KEY. */
	private static final String SCHEMA_LANGUAGE_KEY = "http://java.sun.com/xml/jaxp/properties/schemaLanguage";

	/** The Constant HTTP_WWW_W3_ORG_2001_XML_SCHEMA. */
	private static final String HTTP_WWW_W3_ORG_2001_XML_SCHEMA = "http://www.w3.org/2001/XMLSchema";

	/**
	 * Gets the document builder factory.
	 *
	 * @param schema
	 *            the schema
	 * @return the document builder factory
	 */
	public static DocumentBuilderFactory getDocumentBuilderFactory(final String schema)
	{
		System.setProperty(DOCUMENT_BUILDER_FACTORY_KEY, DOCUMENT_BUILDER_FACTORY_VALUE);
		final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
		factory.setNamespaceAware(true);
		factory.setValidating(true);
		factory.setAttribute(SCHEMA_LANGUAGE_KEY, HTTP_WWW_W3_ORG_2001_XML_SCHEMA);
		factory.setAttribute(SCHEMA_SOURCE_KEY, schema);
		return factory;
	}

	/**
	 * Gets the dOM source.
	 *
	 * @param xml
	 *            the xml
	 * @param errorHandler
	 *            the error handler
	 * @return the dOM source
	 * @throws SAXException
	 *             If a SAX error occurs during parsing.
	 * @throws ParserConfigurationException
	 *             if a DocumentBuilder cannot be created which satisfies the configuration
	 *             requested.
	 * @throws IOException
	 *             Signals that an I/O exception has occurred.
	 */
	public static DOMSource getDOMSource(final File xml, final ErrorHandler errorHandler)
		throws SAXException, ParserConfigurationException, IOException
	{
		return new DOMSource(parse(xml, errorHandler));
	}

	/**
	 * Gets the schema.
	 *
	 * @param xsd
	 *            the xsd
	 * @param errorHandler
	 *            the error handler
	 * @return the schema
	 * @throws SAXException
	 *             If a SAX error occurs during parsing.
	 */
	public static Schema getSchema(final File xsd, final ErrorHandler errorHandler)
		throws SAXException
	{
		// Create a new instance for an XSD-aware SchemaFactory
		final SchemaFactory schemaFactory = SchemaFactory
			.newInstance(HTTP_WWW_W3_ORG_2001_XML_SCHEMA);

		// Set the ErrorHandler implementation.
		schemaFactory.setErrorHandler(errorHandler);

		// get the custom xsd schema that describes
		// the required format for my XML files.
		return schemaFactory.newSchema(xsd);
	}

	/**
	 * Parses the.
	 *
	 * @param xml
	 *            the xml
	 * @param errorHandler
	 *            the error handler
	 * @return the document
	 * @throws SAXException
	 *             If a SAX error occurs during parsing.
	 * @throws ParserConfigurationException
	 *             if a DocumentBuilder cannot be created which satisfies the configuration
	 *             requested.
	 * @throws IOException
	 *             Signals that an I/O exception has occurred.
	 */
	public static Document parse(final File xml, final ErrorHandler errorHandler)
		throws SAXException, ParserConfigurationException, IOException
	{
		final DocumentBuilderFactory factory = getDocumentBuilderFactory(xml.getName());
		final DocumentBuilder builder = factory.newDocumentBuilder();
		builder.setErrorHandler(errorHandler);
		return builder.parse(xml);
	}

	/**
	 * Validate xml through xsd.
	 *
	 * @param xsd
	 *            the xsd
	 * @param xml
	 *            the xml
	 * @param errorHandler
	 *            the error handler
	 * @throws SAXException
	 *             If a SAX error occurs during parsing.
	 * @throws ParserConfigurationException
	 *             if a DocumentBuilder cannot be created which satisfies the configuration
	 *             requested.
	 * @throws IOException
	 *             Signals that an I/O exception has occurred.
	 */
	public static void validateSchema(final File xsd, final File xml,
		final ErrorHandler errorHandler)
		throws SAXException, ParserConfigurationException, IOException
	{

		final Schema schemaXSD = getSchema(xsd, errorHandler);

		// Create a Validator capable of validating XML files according to my custom schema.
		final Validator validator = schemaXSD.newValidator();

		// parse the XML DOM tree againts the stricter XSD schema
		validator.validate(getDOMSource(xml, errorHandler));
	}

	/**
	 * Validate given xml schema.
	 *
	 * @param SchemaUrl
	 *            the schema url
	 * @param XmlDocumentUrl
	 *            the xml document url
	 * @return true if the given xml is valid otherwise false
	 * @throws SAXException
	 *             If a SAX error occurs during parsing.
	 * @throws ParserConfigurationException
	 *             if a DocumentBuilder cannot be created which satisfies the configuration
	 *             requested.
	 * @throws IOException
	 *             Signals that an I/O exception has occurred.
	 */
	public static boolean validateSchema(final String SchemaUrl, final String XmlDocumentUrl)
		throws SAXException, ParserConfigurationException, IOException
	{
		System.setProperty(DOCUMENT_BUILDER_FACTORY_KEY, DOCUMENT_BUILDER_FACTORY_VALUE);
		final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
		factory.setNamespaceAware(true);
		factory.setValidating(true);
		factory.setAttribute(SCHEMA_LANGUAGE_KEY, HTTP_WWW_W3_ORG_2001_XML_SCHEMA);
		factory.setAttribute(SCHEMA_SOURCE_KEY, SchemaUrl);
		final DocumentBuilder builder = factory.newDocumentBuilder();
		final ValidatorHandler handler = new ValidatorHandler();
		builder.setErrorHandler(handler);
		builder.parse(XmlDocumentUrl);
		if (handler.isValid())
		{
			return false;
		}
		return true;
	}

}


/*
 * Copyright 2014 Jacopo Aliprandi, Dario Archetti
 *
 * This file is part of SPF.
 *
 * SPF is free software: you can redistribute it and/or modify it under the
 * terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option)
 * any later version.
 *
 * SPF is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
 * more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with SPF.  If not, see <http://www.gnu.org/licenses/>.
 *
 */
package it.polimi.spf.shared.model;

import android.os.Parcel;
import android.util.Log;

import org.json.JSONException;
import org.json.JSONObject;

/**
 * TODO #Documentation
 *
 * @author Jacopo Aliprandi
 */
public final class SPFActionIntent extends SPFAction {

    private static final String KEY_ACTION = "action";
    private static final String TAG = "SPFActionIntent";
    private final int mType = TYPE_INTENT;
    private final String mAction;

	/*
	 * spf add some arguments in the intent. Use this constant to retrieve the information from the received intent
	 */
    /**
     * the spf identifier of the entity that activated the trigger
     */
    public static final String ARG_STRING_TARGET = "targetid";

    /**
     * the display name name of the entity that activated the trigger
     */
    public static final String ARG_STRING_DISPLAY_NAME = "displayname";

    /**
     * the name of the trigger
     */
    public static final String ARG_STRING_TRIGGER_NAME = "trigger_name";

    /**
     * the id of the trigger
     */
    public static final String ARG_LONG_TRIGGER_ID = "trigger_id";

    /**
     * Constructs a new instance provided the intent action
     *
     * @param action - the intent action
     */
    public SPFActionIntent(String action) {
        if (action == null) {
            throw new NullPointerException("action must not be null");
        }

        this.mAction = action;
    }

    /*
     * Constructor to create instances from parcels.
     */
    private SPFActionIntent(Parcel source) {
        mAction = source.readString();
    }

    public SPFActionIntent(JSONObject source) {
        try {
            mAction = source.getString(KEY_ACTION);
        } catch (JSONException e) {
            throw new IllegalArgumentException("JSON does not represent a valid SPFActionIntent", e);
        }
    }

    public String getAction() {
        return mAction;
    }

    /*
     * (non-Javadoc)
     *
     * @see android.os.Parcelable#describeContents()
     */
    @Override
    public int describeContents() {
        return 0;
    }

    /*
     * (non-Javadoc)
     *
     * @see android.os.Parcelable#writeToParcel(android.os.Parcel, int)
     */
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(mAction);
    }

    /**
     * {@link android.os.Parcelable.Creator} for the {@link android.os.Parcelable} pattern.
     */
    public static final Creator<SPFActionIntent> CREATOR = new Creator<SPFActionIntent>() {

        @Override
        public SPFActionIntent[] newArray(int size) {
            return new SPFActionIntent[size];
        }

        @Override
        public SPFActionIntent createFromParcel(Parcel source) {
            return new SPFActionIntent(source);
        }
    };

    /*
     * (non-Javadoc)
     *
     * @see it.polimi.spf.framework.local.SPFAction#toJSON()
     */
    @Override
    public String toJSON() {
        JSONObject o = new JSONObject();
        try {
            o.put(KEY_TYPE, mType);
            o.put(KEY_ACTION, mAction);
            return o.toString();
        } catch (JSONException e) {
            Log.e(TAG, "Error marshalling action:", e);
            return "";
        }
    }
}


/**
 * The MIT License
 *
 * Copyright (C) 2015 Asterios Raptis
 *
 * 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 de.alpharogroup.xml.tag;

import java.util.Map;
import java.util.Optional;

import lombok.experimental.UtilityClass;

/**
 * The class {@link TagExtensions}.
 */
@UtilityClass
public final class TagExtensions
{

	/**
	 * Creates an {@link Optional} of {@link String} from the given attributes map
	 *
	 * @param attributes
	 *            the attributes map
	 * @return the {@link Optional} of {@link String}
	 */
	public static Optional<String> attributesToString(Map<String, String> attributes)
	{
		StringBuilder builder = new StringBuilder();
		if (attributes != null && !attributes.isEmpty())
		{
			builder.append(" ");
			for (final Map.Entry<String, String> entry : attributes.entrySet())
			{
				builder.append(entry.getKey());
				builder.append("=");
				builder.append("\"").append(entry.getValue()).append("\"");
				builder.append(" ");
			}
		}
		else
		{
			return Optional.empty();
		}
		return Optional.of(builder.toString());
	}
}