// Path: core/src/main/java/com/github/jeluard/stone/spi/Dispatcher.java
// public abstract class Dispatcher {
// 
//   /**
//    * Intercept {@link Exception} thrown when a {@code value} is published.
//    */
//   public interface ExceptionHandler {
// 
//     /**
//      * Invoked when an {@link Exception} is thrown during accumulation/persistency process.
//      *
//      * @param exception
//      */
//     void onException(Exception exception);
// 
//   }
// 
//   static final Logger LOGGER = Loggers.create("dispatcher");
// 
//   private final ExceptionHandler exceptionHandler;
//   protected static final ExceptionHandler DEFAULT_EXCEPTION_HANDLER = new ExceptionHandler() {
//     @Override
//     public void onException(final Exception exception) {
//       if (Dispatcher.LOGGER.isLoggable(Level.WARNING)) {
//         Dispatcher.LOGGER.log(Level.WARNING, "Got exception while publishing", exception);
//       }
//     }
//   };
// 
//   /**
//    * @param exceptionHandler 
//    */
//   public Dispatcher(final ExceptionHandler exceptionHandler) {
//     this.exceptionHandler = Preconditions.checkNotNull(exceptionHandler, "null exceptionHandler");
//   }
// 
//   /**
//    * Safely execute {@link ExceptionHandler#onException(java.lang.Exception)} 
//    *
//    * @param exception 
//    */
//   protected final void notifyExceptionHandler(final Exception exception) {
//     try {
//       this.exceptionHandler.onException(exception);
//     } catch (Exception e) {
//       if (Dispatcher.LOGGER.isLoggable(Level.WARNING)) {
//         Dispatcher.LOGGER.log(Level.WARNING, "Got exception while executing "+ExceptionHandler.class.getSimpleName()+" <"+this.exceptionHandler+">", e);
//       }
//     }
//   }
// 
//   /**
//    * Dispatch {@link Listener}s executions.
//    *
//    * @param previousTimestamp {@linkplain com.github.jeluard.stone.api.TimeSeries#DEFAULT_LATEST_TIMESTAMP} if does not exist
//    * @param currentTimestamp
//    * @param value
//    * @param listeners
//    * @return true if dispatch has been accepted; false if rejected
//    */
//    public abstract boolean dispatch(long previousTimestamp, long currentTimestamp, int value, Listener[] listeners);
// 
// }


import javax.annotation.concurrent.NotThreadSafe;
import com.github.jeluard.guayaba.annotation.Idempotent;
import com.github.jeluard.guayaba.lang.Identifiable;
import com.github.jeluard.stone.spi.Dispatcher;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import java.io.Closeable;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
/**
 * Copyright 2012 Julien Eluard
 * This project includes software developed by Julien Eluard: https://github.com/jeluard/
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.github.jeluard.stone.api;




/**
 * Main abstraction allowing to publish {@code timestamp}/{@code value} pair. Provided {@code timestamp} must monotonically increased and compatible with {@code granularity}.
 * <br>
 * If {@code granularity} is {@code 1} then {@code timestamp} can be any value (as long as it follows the monotonicity rule).
 * <br>
 * If {@code granularity} is greater than {@code 1} each successive {@code timestamp}s must have {@code granularity - 1} values in between. Note this does not imply only {@code granularity} multiples are accepted as there can be more than {@code granularity - 1} in between two timestamps.
 * <br>
 * An invalid {@code timestamp} will be signaled via return value of {@link #publish(long, int)}.
 * <br>
 * <br>
 * Each accepted value then is passed to all associated {@link Listener}.
 * <br>
 * <br>
 * Once {@link #close()} has been called {@link #publish(long, int)} behaviour is undefined.
 * <br>
 * <br>
 * <b>WARNING</b> {@link TimeSeries} is not threadsafe and not supposed to be manipulated concurrently from different threads. Note it can be manipulated by different threads as long as you ensure they don't call {@link #publish(long, int)} concurrently.
 */
@NotThreadSafe
public class TimeSeries implements Identifiable<String>, Closeable {

  private static final Set<String> IDS = new CopyOnWriteArraySet<String>();

  private final String id;
  private final int granularity;
  private final Listener[] listeners;
// Path: core/src/main/java/com/github/jeluard/stone/spi/Dispatcher.java
// public abstract class Dispatcher {
// 
//   /**
//    * Intercept {@link Exception} thrown when a {@code value} is published.
//    */
//   public interface ExceptionHandler {
// 
//     /**
//      * Invoked when an {@link Exception} is thrown during accumulation/persistency process.
//      *
//      * @param exception
//      */
//     void onException(Exception exception);
// 
//   }
// 
//   static final Logger LOGGER = Loggers.create("dispatcher");
// 
//   private final ExceptionHandler exceptionHandler;
//   protected static final ExceptionHandler DEFAULT_EXCEPTION_HANDLER = new ExceptionHandler() {
//     @Override
//     public void onException(final Exception exception) {
//       if (Dispatcher.LOGGER.isLoggable(Level.WARNING)) {
//         Dispatcher.LOGGER.log(Level.WARNING, "Got exception while publishing", exception);
//       }
//     }
//   };
// 
//   /**
//    * @param exceptionHandler 
//    */
//   public Dispatcher(final ExceptionHandler exceptionHandler) {
//     this.exceptionHandler = Preconditions.checkNotNull(exceptionHandler, "null exceptionHandler");
//   }
// 
//   /**
//    * Safely execute {@link ExceptionHandler#onException(java.lang.Exception)} 
//    *
//    * @param exception 
//    */
//   protected final void notifyExceptionHandler(final Exception exception) {
//     try {
//       this.exceptionHandler.onException(exception);
//     } catch (Exception e) {
//       if (Dispatcher.LOGGER.isLoggable(Level.WARNING)) {
//         Dispatcher.LOGGER.log(Level.WARNING, "Got exception while executing "+ExceptionHandler.class.getSimpleName()+" <"+this.exceptionHandler+">", e);
//       }
//     }
//   }
// 
//   /**
//    * Dispatch {@link Listener}s executions.
//    *
//    * @param previousTimestamp {@linkplain com.github.jeluard.stone.api.TimeSeries#DEFAULT_LATEST_TIMESTAMP} if does not exist
//    * @param currentTimestamp
//    * @param value
//    * @param listeners
//    * @return true if dispatch has been accepted; false if rejected
//    */
//    public abstract boolean dispatch(long previousTimestamp, long currentTimestamp, int value, Listener[] listeners);
// 
// }


// Path: core/src/main/java/com/github/jeluard/stone/api/TimeSeries.java
import javax.annotation.concurrent.NotThreadSafe;
import com.github.jeluard.guayaba.annotation.Idempotent;
import com.github.jeluard.guayaba.lang.Identifiable;
import com.github.jeluard.stone.spi.Dispatcher;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import java.io.Closeable;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;

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




/**
 * Main abstraction allowing to publish {@code timestamp}/{@code value} pair. Provided {@code timestamp} must monotonically increased and compatible with {@code granularity}.
 * <br>
 * If {@code granularity} is {@code 1} then {@code timestamp} can be any value (as long as it follows the monotonicity rule).
 * <br>
 * If {@code granularity} is greater than {@code 1} each successive {@code timestamp}s must have {@code granularity - 1} values in between. Note this does not imply only {@code granularity} multiples are accepted as there can be more than {@code granularity - 1} in between two timestamps.
 * <br>
 * An invalid {@code timestamp} will be signaled via return value of {@link #publish(long, int)}.
 * <br>
 * <br>
 * Each accepted value then is passed to all associated {@link Listener}.
 * <br>
 * <br>
 * Once {@link #close()} has been called {@link #publish(long, int)} behaviour is undefined.
 * <br>
 * <br>
 * <b>WARNING</b> {@link TimeSeries} is not threadsafe and not supposed to be manipulated concurrently from different threads. Note it can be manipulated by different threads as long as you ensure they don't call {@link #publish(long, int)} concurrently.
 */
@NotThreadSafe
public class TimeSeries implements Identifiable<String>, Closeable {

  private static final Set<String> IDS = new CopyOnWriteArraySet<String>();

  private final String id;
  private final int granularity;
  private final Listener[] listeners;
  private final Dispatcher dispatcher;