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.http.classic.entity;
018
019import static org.apache.juneau.commons.utils.Shorts.*;
020
021import java.io.*;
022
023import org.apache.juneau.http.*;
024import org.apache.juneau.http.classic.header.*;
025
026/**
027 * A repeatable entity that obtains its content from a byte array.
028 *
029 * <h5 class='section'>See Also:</h5><ul>
030 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauRestCommon">juneau-rest-common</a>
031 * </ul>
032 */
033@SuppressWarnings({
034   "java:S1192", // Duplicated literals (argument/property names) read more clearly inline than as constants
035   "resource" // Resource management handled externally
036})
037public class ByteArrayEntity extends BasicHttpEntity<ByteArrayEntity> {
038
039   private static final byte[] EMPTY = {};
040
041   /**
042    * Constructor.
043    */
044   public ByteArrayEntity() {}
045
046   /**
047    * Constructor.
048    *
049    * @param contentType The entity content type.  Can be <jk>null</jk> to omit an explicit <c>Content-Type</c> header.
050    * @param contents The entity contents.  Can be <jk>null</jk> (treated as an empty byte array).
051    */
052   public ByteArrayEntity(ContentType contentType, byte[] contents) {
053      super(contentType, contents);
054   }
055
056   /**
057    * Copy constructor.
058    *
059    * @param copyFrom The bean being copied.  Must not be <jk>null</jk>.
060    */
061   protected ByteArrayEntity(ByteArrayEntity copyFrom) {
062      super(copyFrom);
063   }
064
065   @Override /* Overridden from AbstractHttpEntity */
066   public byte[] asBytes() throws IOException {
067      return cp(content());
068   }
069
070   @Override /* Overridden from AbstractHttpEntity */
071   public String asString() throws IOException {
072      return new String(content(), getCharset());
073   }
074
075   @Override /* Overridden from BasicHttpEntity */
076   public ByteArrayEntity copy() {
077      return new ByteArrayEntity(this);
078   }
079
080   @Override /* Overridden from HttpEntity */
081   public InputStream getContent() throws IOException { return new ByteArrayInputStream(content()); }
082
083   @Override /* Overridden from HttpEntity */
084   public long getContentLength() { return isSupplied() ? super.getContentLength() : content().length; }
085
086   @Override /* Overridden from HttpEntity */
087   public boolean isRepeatable() { return true; }
088
089   @Override /* Overridden from BasicHttpEntity */
090   public ByteArrayEntity unmodifiable() {
091      return this instanceof UnmodifiableBean ? this : new Unmodifiable(this);
092   }
093
094   @Override /* Overridden from HttpEntity */
095   public void writeTo(OutputStream out) throws IOException {
096      reqnn("out", out);
097      out.write(content());
098   }
099
100   @Override /* Overridden from Object */
101   public boolean equals(Object o) {
102      // Leaf-type gate: keeps this entity from comparing equal to a different leaf with equal base content, while
103      // preserving content equality (ByteArrayEntity.Unmodifiable IS-A ByteArrayEntity, so bean.equals(bean.unmodifiable()) holds).
104      // This leaf adds no equality-relevant state of its own; base content is compared via super.equals(...).
105      return o instanceof ByteArrayEntity && super.equals(o);
106   }
107
108   @Override /* Overridden from Object */
109   public int hashCode() {
110      // Fold in the leaf type so distinct leaves land in different buckets; stable across ByteArrayEntity and its
111      // Unmodifiable snapshot (both report ByteArrayEntity.class), keeping the equals/hashCode contract intact.
112      return h(ByteArrayEntity.class, super.hashCode());
113   }
114
115   private byte[] content() {
116      return contentOrElse(EMPTY);
117   }
118
119   /**
120    * Unmodifiable point-in-time snapshot of the enclosing {@link ByteArrayEntity}.
121    *
122    * <p>
123    * Its only behavioral override is {@link #modify(Runnable)}, which throws — because all mutation is funneled through
124    * {@code modify(...)}, this single override freezes the entire mutation surface.
125    */
126   public static class Unmodifiable extends ByteArrayEntity implements UnmodifiableBean {
127
128      /**
129       * Constructor.
130       *
131       * @param copyFrom The entity to snapshot.  Must not be <jk>null</jk>.
132       */
133      @SuppressWarnings({
134         "java:S1699" // Paradigm intentionally calls the overridable freeze() from the ctor to deep-freeze sub-beans.
135      })
136      protected Unmodifiable(ByteArrayEntity copyFrom) {
137         super(copyFrom);
138         freeze();
139      }
140
141      @Override /* Overridden from BasicHttpEntity */
142      protected ByteArrayEntity modify(Runnable mutation) {
143         throw uoex("Bean is unmodifiable.");
144      }
145   }
146}