// Path: src/main/java/org/calrissian/flowmix/api/Aggregator.java
// public interface Aggregator extends Serializable {
// 
//     public static final String GROUP_BY = "groupBy";
//     public static final String GROUP_BY_DELIM = "\u0000";
// 
//     void configure(Map<String, String> configuration);
// 
//     void added(WindowItem item);
// 
//     void evicted(WindowItem item);
// 
//     List<AggregatedEvent> aggregate();
// }
// 
// Path: src/main/java/org/calrissian/flowmix/core/model/event/AggregatedEvent.java
// public class AggregatedEvent {
// 
//   private Event event;
//   private String previousStream;
// 
//   public AggregatedEvent(Event event, String previousStream) {
//     this.event = event;
//     this.previousStream = previousStream;
//   }
// 
//   public AggregatedEvent(Event event) {
//     this.event = event;
//   }
// 
//   public Event getEvent() {
//     return event;
//   }
// 
//   public String getPreviousStream() {
//     return previousStream;
//   }
// 
//   @Override
//   public boolean equals(Object o) {
//     if (this == o) return true;
//     if (o == null || getClass() != o.getClass()) return false;
// 
//     AggregatedEvent that = (AggregatedEvent) o;
// 
//     if (event != null ? !event.equals(that.event) : that.event != null) return false;
//     if (previousStream != null ? !previousStream.equals(that.previousStream) : that.previousStream != null)
//       return false;
// 
//     return true;
//   }
// 
//   @Override
//   public int hashCode() {
//     int result = event != null ? event.hashCode() : 0;
//     result = 31 * result + (previousStream != null ? previousStream.hashCode() : 0);
//     return result;
//   }
// 
//   @Override
//   public String toString() {
//     return "AggregatedEvent{" +
//             "event=" + event +
//             ", previousStream='" + previousStream + '\'' +
//             '}';
//   }
// }
// 
// Path: src/main/java/org/calrissian/flowmix/core/support/window/WindowItem.java
// public class WindowItem {
//     Event event;
//     long timestamp;
//     String previousStream;
// 
//     public WindowItem(Event event, long timestamp, String previousStream) {
//         this.event = event;
//         this.timestamp = timestamp;
//         this.previousStream = previousStream;
//     }
// 
//     public Event getEvent() {
//         return event;
//     }
// 
//     public long getTimestamp() {
//         return timestamp;
//     }
// 
//     public String getPreviousStream() {
//       return previousStream;
//     }
// 
//     @Override
//     public String toString() {
//         return "WindowItem{" +
//                 "event=" + event +
//                 ", timestamp=" + timestamp +
//                 '}';
//     }
// }
// 
// Path: src/main/java/org/calrissian/flowmix/exceptions/FlowmixException.java
// public class FlowmixException extends RuntimeException {
// 
//     public FlowmixException(String msg, Throwable t) {
//         super(msg, t);
//     }
// 
// }


import org.calrissian.mango.domain.event.BaseEvent;
import org.calrissian.mango.domain.event.Event;
import static java.lang.System.currentTimeMillis;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static java.util.UUID.randomUUID;
import org.apache.commons.lang.StringUtils;
import org.calrissian.flowmix.api.Aggregator;
import static org.calrissian.flowmix.api.Aggregator.GROUP_BY;
import static org.calrissian.flowmix.api.Aggregator.GROUP_BY_DELIM;
import org.calrissian.flowmix.core.model.event.AggregatedEvent;
import org.calrissian.flowmix.core.support.window.WindowItem;
import org.calrissian.flowmix.exceptions.FlowmixException;
import org.calrissian.mango.domain.Tuple;
/*
 * Copyright 2015 The Calrissian Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.calrissian.flowmix.api.aggregator;


/**
 *
 * Abstract aggregator for simple implementations
 *
 * @param <T> Aggregation result type
 * @param <F> Field type
 */
public abstract class AbstractAggregator<T, F> implements Aggregator {

    /**
     * field to operate with
     */
    public static final String OPERATED_FIELD = "operatedField";

    /**
     * output field
     */
    public static final String OUTPUT_FIELD = "outputField";

    /**
     * grouped fields description
     */
    protected Map<String, Collection<Tuple>> groupedValues;

    /**
     * fields to group by
     */
    protected String[] groupByFields;

    /**
     * operated field name
     */
    protected String operatedField;

    /**
     * output field set by implementation
     */
    protected String outputField = getOutputField();

    /**
     *
     * @return output field implementation
     */
    protected abstract String getOutputField();

    /**
     *
     * @param configuration
     */
    @Override
    public void configure(Map<String, String> configuration) {
        if (configuration.get(GROUP_BY) != null) {
            groupByFields = StringUtils.splitPreserveAllTokens(configuration.get(GROUP_BY), GROUP_BY_DELIM);
        }
        if (configuration.get(OUTPUT_FIELD) != null) {
            outputField = configuration.get(OUTPUT_FIELD);
        }
        if (configuration.get(OPERATED_FIELD) != null) {
            operatedField = configuration.get(OPERATED_FIELD);
        } else {
            throw new RuntimeException("Aggregator needs a field to operate it. Property missing: " + OPERATED_FIELD);
        }
    }

    /**
     *
     * @param fieldValue field value to work with after item is added to grouped
     * values
     */
    public abstract void add(F fieldValue);

    /**
     *
     * @param item
     * @throws org.calrissian.flowmix.exceptions.FlowmixException
     */
    @Override
// Path: src/main/java/org/calrissian/flowmix/api/Aggregator.java
// public interface Aggregator extends Serializable {
// 
//     public static final String GROUP_BY = "groupBy";
//     public static final String GROUP_BY_DELIM = "\u0000";
// 
//     void configure(Map<String, String> configuration);
// 
//     void added(WindowItem item);
// 
//     void evicted(WindowItem item);
// 
//     List<AggregatedEvent> aggregate();
// }
// 
// Path: src/main/java/org/calrissian/flowmix/core/model/event/AggregatedEvent.java
// public class AggregatedEvent {
// 
//   private Event event;
//   private String previousStream;
// 
//   public AggregatedEvent(Event event, String previousStream) {
//     this.event = event;
//     this.previousStream = previousStream;
//   }
// 
//   public AggregatedEvent(Event event) {
//     this.event = event;
//   }
// 
//   public Event getEvent() {
//     return event;
//   }
// 
//   public String getPreviousStream() {
//     return previousStream;
//   }
// 
//   @Override
//   public boolean equals(Object o) {
//     if (this == o) return true;
//     if (o == null || getClass() != o.getClass()) return false;
// 
//     AggregatedEvent that = (AggregatedEvent) o;
// 
//     if (event != null ? !event.equals(that.event) : that.event != null) return false;
//     if (previousStream != null ? !previousStream.equals(that.previousStream) : that.previousStream != null)
//       return false;
// 
//     return true;
//   }
// 
//   @Override
//   public int hashCode() {
//     int result = event != null ? event.hashCode() : 0;
//     result = 31 * result + (previousStream != null ? previousStream.hashCode() : 0);
//     return result;
//   }
// 
//   @Override
//   public String toString() {
//     return "AggregatedEvent{" +
//             "event=" + event +
//             ", previousStream='" + previousStream + '\'' +
//             '}';
//   }
// }
// 
// Path: src/main/java/org/calrissian/flowmix/core/support/window/WindowItem.java
// public class WindowItem {
//     Event event;
//     long timestamp;
//     String previousStream;
// 
//     public WindowItem(Event event, long timestamp, String previousStream) {
//         this.event = event;
//         this.timestamp = timestamp;
//         this.previousStream = previousStream;
//     }
// 
//     public Event getEvent() {
//         return event;
//     }
// 
//     public long getTimestamp() {
//         return timestamp;
//     }
// 
//     public String getPreviousStream() {
//       return previousStream;
//     }
// 
//     @Override
//     public String toString() {
//         return "WindowItem{" +
//                 "event=" + event +
//                 ", timestamp=" + timestamp +
//                 '}';
//     }
// }
// 
// Path: src/main/java/org/calrissian/flowmix/exceptions/FlowmixException.java
// public class FlowmixException extends RuntimeException {
// 
//     public FlowmixException(String msg, Throwable t) {
//         super(msg, t);
//     }
// 
// }


// Path: src/main/java/org/calrissian/flowmix/api/aggregator/AbstractAggregator.java
import org.calrissian.mango.domain.event.BaseEvent;
import org.calrissian.mango.domain.event.Event;
import static java.lang.System.currentTimeMillis;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static java.util.UUID.randomUUID;
import org.apache.commons.lang.StringUtils;
import org.calrissian.flowmix.api.Aggregator;
import static org.calrissian.flowmix.api.Aggregator.GROUP_BY;
import static org.calrissian.flowmix.api.Aggregator.GROUP_BY_DELIM;
import org.calrissian.flowmix.core.model.event.AggregatedEvent;
import org.calrissian.flowmix.core.support.window.WindowItem;
import org.calrissian.flowmix.exceptions.FlowmixException;
import org.calrissian.mango.domain.Tuple;

/*
 * Copyright 2015 The Calrissian Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.calrissian.flowmix.api.aggregator;


/**
 *
 * Abstract aggregator for simple implementations
 *
 * @param <T> Aggregation result type
 * @param <F> Field type
 */
public abstract class AbstractAggregator<T, F> implements Aggregator {

    /**
     * field to operate with
     */
    public static final String OPERATED_FIELD = "operatedField";

    /**
     * output field
     */
    public static final String OUTPUT_FIELD = "outputField";

    /**
     * grouped fields description
     */
    protected Map<String, Collection<Tuple>> groupedValues;

    /**
     * fields to group by
     */
    protected String[] groupByFields;

    /**
     * operated field name
     */
    protected String operatedField;

    /**
     * output field set by implementation
     */
    protected String outputField = getOutputField();

    /**
     *
     * @return output field implementation
     */
    protected abstract String getOutputField();

    /**
     *
     * @param configuration
     */
    @Override
    public void configure(Map<String, String> configuration) {
        if (configuration.get(GROUP_BY) != null) {
            groupByFields = StringUtils.splitPreserveAllTokens(configuration.get(GROUP_BY), GROUP_BY_DELIM);
        }
        if (configuration.get(OUTPUT_FIELD) != null) {
            outputField = configuration.get(OUTPUT_FIELD);
        }
        if (configuration.get(OPERATED_FIELD) != null) {
            operatedField = configuration.get(OPERATED_FIELD);
        } else {
            throw new RuntimeException("Aggregator needs a field to operate it. Property missing: " + OPERATED_FIELD);
        }
    }

    /**
     *
     * @param fieldValue field value to work with after item is added to grouped
     * values
     */
    public abstract void add(F fieldValue);

    /**
     *
     * @param item
     * @throws org.calrissian.flowmix.exceptions.FlowmixException
     */
    @Override
    public void added(WindowItem item){