001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.juneau.marshall.sse;
018
019
020import java.io.*;
021import java.util.*;
022
023import org.apache.juneau.marshall.*;
024import org.apache.juneau.marshall.parser.*;
025import org.apache.juneau.marshall.stream.*;
026import static org.apache.juneau.commons.utils.Shorts.*;
027
028/**
029 * Session object that lives for the duration of a single use of {@link SseParser}.
030 *
031 * <h5 class='section'>Notes:</h5><ul>
032 *    <li class='warn'>This class is not thread safe and is typically discarded after one use.
033 * </ul>
034 *
035 * <h5 class='section'>See Also:</h5><ul>
036 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/Sse">SSE</a>
037 * </ul>
038 */
039@SuppressWarnings({
040   "java:S110", // Inheritance depth acceptable for session hierarchy
041   "java:S1192", // Duplicated literals (argument/property names) read more clearly inline than as constants
042   "resource", // Closeable resources are owned by the caller's parser session; Eclipse JDT @Owning warning is by design.
043   "unchecked" // Type erasure: SseEvent/List<SseEvent> returned via Object cast
044})
045public class SseParserSession extends ReaderParserSession implements RecordReadable {
046
047   /**
048    * Builder class.
049    */
050   public static class Builder extends ReaderParserSession.Builder<Builder> {
051
052      /**
053       * Constructor.
054       *
055       * @param ctx The context creating this session. Cannot be <jk>null</jk>.
056       */
057      protected Builder(SseParser ctx) {
058         super(reqnn("ctx", ctx));
059      }
060
061      @Override
062      public SseParserSession build() {
063         return new SseParserSession(this);
064      }
065   }
066
067   /**
068    * Creates a new builder for this object.
069    *
070    * @param ctx The context creating this session. Cannot be <jk>null</jk>.
071    * @return A new builder.
072    */
073   public static Builder create(SseParser ctx) {
074      return new Builder(reqnn("ctx", ctx));
075   }
076
077   /**
078    * Constructor.
079    *
080    * @param builder The builder for this object.
081    */
082   protected SseParserSession(Builder builder) {
083      super(builder);
084   }
085
086   @Override /* RecordReadable */
087   public RecordReader readRecords(Object input) throws IOException {
088      return RecordAdapter.arrayReader(this, input);
089   }
090
091   @Override /* RecordReadable */
092   public boolean isRecordStreaming() {
093      return false;
094   }
095
096   @Override /* Overridden from ParserSession */
097   @SuppressWarnings({
098      "java:S2095" // Reader is owned by ParserPipe and closed via SseEventReader in try-with-resources.
099   })
100   protected <T> T doRead(ParserPipe pipe, ClassMeta<T> type) throws IOException, ParseException {
101      var r = pipe.getReader();
102      if (r == null)
103         return null;
104      List<SseEvent> events;
105      try (var r2 = new SseEventReader(r)) {
106         events = r2.toList();
107      }
108      if (type == null || type.isObject() || type.isCollectionOrArray()) {
109         if (type != null && type.isArray())
110            return (T) events.toArray(new SseEvent[0]);
111         return (T) events;
112      }
113      if (SseEvent.class.equals(type.inner())) {
114         return events.isEmpty() ? null : (T) events.get(0);
115      }
116      if (List.class.isAssignableFrom(type.inner()))
117         return (T) events;
118      throw new ParseException(this, "SseParser cannot produce target type '%s'.", type);
119   }
120}