content: package act;
  
  /*-
   * #%L
   * ACT Framework
   * %%
   * Copyright (C) 2014 - 2017 ActFramework
   * %%
   * 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.
   * #L%
   */
  
  import act.app.ActionContext;
  import act.conf.AppConfig;
  import org.osgl.$;
  import org.osgl.http.H;
  import org.osgl.mvc.MvcConfig;
  import org.osgl.mvc.result.Ok;
  import org.osgl.mvc.result.Redirect;
  import org.osgl.storage.impl.SObject;
  import org.osgl.util.E;
  import org.osgl.util.IO;
  import org.osgl.util.S;
  
  import java.io.File;
  import java.net.URL;
  import java.util.Locale;
  
  public abstract class ActResponse<T extends ActResponse> extends H.Response<T> {
  
      private boolean onResult;
      private boolean closed;
      protected String charset;
      protected Locale locale;
      protected String contentType;
      protected H.Format fmt;
      private boolean charsetSet;
      private boolean committed;
      protected int statusCode = -1;
  
      protected ActResponse() {}
  
      protected ActResponse(AppConfig config) {
          charset = config.encoding();
          locale = config.locale();
      }
  
      @Override
      public String characterEncoding() {
          return charset;
      }
  
      @Override
      public T characterEncoding(String encoding) {
          charset = encoding;
          charsetSet = true;
          return me();
      }
  
      public T send(URL url) {
          String protocol = url.getProtocol();
          if ("file".equals(protocol)) {
              return send(new File(url.getPath()));
          }
          writeBinary(SObject.of(IO.inputStream(url)));
          return me();
      }
  
      public T send(File file) {
          writeBinary(SObject.of(file));
          return me();
      }
  
      @Override
      public T sendError(int sc, String msg) {
          throw E.unsupport();
      }
  
      @Override
      public T sendError(int sc) {
          throw E.unsupport();
      }
  
      @Override
      public T sendRedirect(String location) {
          throw new Redirect(location);
      }
  
      public T contentType(H.Format fmt) {
          contentType(fmt.contentType());
          this.fmt = fmt;
          return me();
      }
  
      public H.Format lastContentType() {
          return fmt;
      }
  
      /**
       * This response is ready for Result evaluation.
       */
      public void onResult() {
         this.onResult = true;
      }
  
      public T commitContentType() {
          header(H.Header.Names.CONTENT_TYPE, _getContentType());
          return me();
      }
  
      @Override
      public void close() {
          super.close();
          markClosed();
      }
  
      public boolean isClosed() {
          return closed;
      }
  
      protected void markClosed() {
          this.closed = true;
          ActionContext ctx = context();
          ctx.markAsReadyForClose();
      }
  
      protected abstract void _setStatusCode(int sc);
  
      @Override
      public T status(int sc) {
          E.illegalArgumentIf(sc < 100, "Invalid status code");
          statusCode = sc;
          _setStatusCode(sc);
          return me();
      }
  
      @Override
      public int statusCode() {
          return statusCode;
      }
  
      public ActResponse beforeWritingContent() {
          if (onResult) {
              return this;
          }
          ActionContext ctx = context();
          ctx.dissolve();
          MvcConfig.applyBeforeCommitResultHandler(Ok.get(), ctx.req(), this);
          return this;
      }
  
      public ActResponse afterWritingContent() {
          if (committed) {
              return this;
          }
          commit();
          ActionContext ctx = context();
          MvcConfig.applyAfterCommitResultHandler(Ok.get(), ctx.req(), this);
          return this;
      }
  
      @Override
      public final void commit() {
          if (isCommitted()) {
              return;
          }
          committed = true;
          doCommit();
      }
  
      protected abstract void doCommit();
  
      protected final boolean isCommitted() {
          return committed;
      }
  
      @Override
      protected void _setContentType(String type) {
          if (S.eq(this.contentType, type)) {
              return;
          }
          this.contentType = type;
          this.fmt = H.Format.resolve(type);
      }
  
      protected String _getContentType() {
          return this.contentType != null ? (this.charsetSet ? this.contentType + ";charset=" + this.charset : this.contentType) : null;
      }
  
      protected final T me() {
          return (T) this;
      }
  
      private ActionContext ctx() {
          return $.cast(this.context());
      }
  }
  