1   /*
2    * Copyright 2018 LINE Corporation
3    *
4    * LINE Corporation licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   https://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  
17  package com.linecorp.centraldogma.common;
18  
19  import static com.google.common.base.Preconditions.checkArgument;
20  import static java.util.Objects.requireNonNull;
21  
22  import java.util.List;
23  
24  import javax.annotation.Nullable;
25  
26  import com.fasterxml.jackson.databind.JsonNode;
27  import com.google.common.base.MoreObjects;
28  import com.google.common.collect.ImmutableList;
29  
30  /**
31   * A merged entry in a repository.
32   *
33   * @param <T> the content type. It is {@link JsonNode} because only JSON merge is currently supported.
34   */
35  public final class MergedEntry<T> implements ContentHolder<T> {
36  
37      /**
38       * Returns a newly-created {@link MergedEntry}.
39       *
40       * @param revision the revision of the {@link MergedEntry}
41       * @param type the type of the {@link MergedEntry}
42       * @param content the content of the {@link MergedEntry}
43       * @param <T> the content type. It is {@link JsonNode} because only JSON merge is currently supported.
44       * @param paths the paths which participated to compose the {@link MergedEntry}
45       */
46      public static <T> MergedEntry<T> of(Revision revision, EntryType type, T content, String... paths) {
47          return new MergedEntry<>(revision, type, content, ImmutableList.copyOf(requireNonNull(paths, "paths")));
48      }
49  
50      /**
51       * Returns a newly-created {@link MergedEntry}.
52       *
53       * @param revision the revision of the {@link MergedEntry}
54       * @param type the type of the {@link MergedEntry}
55       * @param content the content of the {@link MergedEntry}
56       * @param <T> the content type. It is {@link JsonNode} because only JSON merge is currently supported.
57       * @param paths the paths which participated to compose the {@link MergedEntry}
58       */
59      public static <T> MergedEntry<T> of(Revision revision, EntryType type, T content, Iterable<String> paths) {
60          return new MergedEntry<>(revision, type, content, paths);
61      }
62  
63      private final Revision revision;
64      private final EntryType type;
65      private final List<String> paths;
66      private final T content;
67      @Nullable
68      private String contentAsText;
69      @Nullable
70      private String contentAsPrettyText;
71  
72      /**
73       * Creates a new instance.
74       */
75      private MergedEntry(Revision revision, EntryType type, T content, Iterable<String> paths) {
76          this.revision = requireNonNull(revision, "revision");
77          this.type = requireNonNull(type, "type");
78          requireNonNull(content, "content");
79          final Class<?> entryType = type.type();
80          checkArgument(entryType.isAssignableFrom(content.getClass()),
81                        "content type: %s (expected: %s)", content.getClass(), entryType);
82          this.content = content;
83          this.paths = ImmutableList.copyOf(requireNonNull(paths, "paths"));
84      }
85  
86      // TODO(minwoox) Move this method upto ContentHolder when we include the revision in Entry as well.
87      /**
88       * Returns the {@link Revision} of this {@link MergedEntry}.
89       */
90      public Revision revision() {
91          return revision;
92      }
93  
94      @Override
95      public EntryType type() {
96          return type;
97      }
98  
99      @Override
100     public T content() {
101         return content;
102     }
103 
104     /**
105      * Returns the paths which participated to compose the {@link MergedEntry}.
106      */
107     public List<String> paths() {
108         return paths;
109     }
110 
111     @Override
112     public String contentAsText() {
113         if (contentAsText == null) {
114             contentAsText = ContentHolder.super.contentAsText();
115         }
116         return contentAsText;
117     }
118 
119     @Override
120     public String contentAsPrettyText() {
121         if (contentAsPrettyText == null) {
122             contentAsPrettyText = ContentHolder.super.contentAsPrettyText();
123         }
124         return contentAsPrettyText;
125     }
126 
127     @Override
128     public int hashCode() {
129         return (revision.hashCode() * 31 + type.hashCode()) * 31 + content.hashCode();
130     }
131 
132     @Override
133     public boolean equals(Object o) {
134         if (this == o) {
135             return true;
136         }
137         if (!(o instanceof MergedEntry)) {
138             return false;
139         }
140         @SuppressWarnings("unchecked")
141         final MergedEntry<T> that = (MergedEntry<T>) o;
142         return revision.equals(that.revision) &&
143                type == that.type &&
144                content.equals(that.content) &&
145                paths.equals(that.paths);
146     }
147 
148     @Override
149     public String toString() {
150         return MoreObjects.toStringHelper(this)
151                           .add("revision", revision())
152                           .add("type", type())
153                           .add("content", contentAsText())
154                           .add("paths", paths())
155                           .toString();
156     }
157 }