| Shawn Pearce | 374f184 | 2015-02-10 15:36:54 -0800 | [diff] [blame] | 1 | // Copyright 2015 Google Inc. All Rights Reserved. |
| 2 | // |
| 3 | // Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | // you may not use this file except in compliance with the License. |
| 5 | // You may obtain a copy of the License at |
| 6 | // |
| 7 | // http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | // |
| 9 | // Unless required by applicable law or agreed to in writing, software |
| 10 | // distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | // See the License for the specific language governing permissions and |
| 13 | // limitations under the License. |
| 14 | |
| 15 | package com.google.gitiles.doc; |
| 16 | |
| Shawn Pearce | 374f184 | 2015-02-10 15:36:54 -0800 | [diff] [blame] | 17 | import static javax.servlet.http.HttpServletResponse.SC_NOT_MODIFIED; |
| 18 | import static org.eclipse.jgit.lib.Constants.OBJ_BLOB; |
| 19 | import static org.eclipse.jgit.lib.FileMode.TYPE_FILE; |
| 20 | import static org.eclipse.jgit.lib.FileMode.TYPE_MASK; |
| 21 | import static org.eclipse.jgit.lib.FileMode.TYPE_TREE; |
| 22 | |
| 23 | import com.google.common.base.MoreObjects; |
| 24 | import com.google.common.base.Strings; |
| Shawn Pearce | 5c34e09 | 2017-06-29 21:18:30 -0700 | [diff] [blame] | 25 | import com.google.common.base.Throwables; |
| Shawn Pearce | 374f184 | 2015-02-10 15:36:54 -0800 | [diff] [blame] | 26 | import com.google.common.hash.Hasher; |
| 27 | import com.google.common.hash.Hashing; |
| 28 | import com.google.common.net.HttpHeaders; |
| 29 | import com.google.gitiles.BaseServlet; |
| Shawn Pearce | 374f184 | 2015-02-10 15:36:54 -0800 | [diff] [blame] | 30 | import com.google.gitiles.GitilesAccess; |
| David Pursehouse | 27cf26d | 2019-05-13 06:48:07 +0200 | [diff] [blame] | 31 | import com.google.gitiles.GitilesRequestFailureException; |
| 32 | import com.google.gitiles.GitilesRequestFailureException.FailureReason; |
| Shawn Pearce | 374f184 | 2015-02-10 15:36:54 -0800 | [diff] [blame] | 33 | import com.google.gitiles.GitilesView; |
| 34 | import com.google.gitiles.Renderer; |
| 35 | import com.google.gitiles.ViewFilter; |
| Shawn Pearce | 5c34e09 | 2017-06-29 21:18:30 -0700 | [diff] [blame] | 36 | import com.google.gitiles.doc.html.StreamHtmlBuilder; |
| Dave Borowitz | 3b744b1 | 2016-08-19 16:11:10 -0400 | [diff] [blame] | 37 | import java.io.IOException; |
| Shawn Pearce | 5c34e09 | 2017-06-29 21:18:30 -0700 | [diff] [blame] | 38 | import java.io.OutputStream; |
| 39 | import java.io.Writer; |
| Dave Borowitz | 3b744b1 | 2016-08-19 16:11:10 -0400 | [diff] [blame] | 40 | import java.util.HashMap; |
| 41 | import java.util.Map; |
| 42 | import javax.annotation.Nullable; |
| 43 | import javax.servlet.http.HttpServletRequest; |
| 44 | import javax.servlet.http.HttpServletResponse; |
| Shawn Pearce | 12c8fab | 2016-05-15 16:55:21 -0700 | [diff] [blame] | 45 | import org.commonmark.node.Node; |
| Shawn Pearce | 374f184 | 2015-02-10 15:36:54 -0800 | [diff] [blame] | 46 | import org.eclipse.jgit.errors.IncorrectObjectTypeException; |
| Shawn Pearce | 47fd656 | 2016-05-28 14:15:15 -0700 | [diff] [blame] | 47 | import org.eclipse.jgit.errors.LargeObjectException; |
| Shawn Pearce | 374f184 | 2015-02-10 15:36:54 -0800 | [diff] [blame] | 48 | import org.eclipse.jgit.http.server.ServletUtils; |
| Shawn Pearce | 374f184 | 2015-02-10 15:36:54 -0800 | [diff] [blame] | 49 | import org.eclipse.jgit.lib.Constants; |
| 50 | import org.eclipse.jgit.lib.ObjectId; |
| Shawn Pearce | 374f184 | 2015-02-10 15:36:54 -0800 | [diff] [blame] | 51 | import org.eclipse.jgit.lib.ObjectReader; |
| 52 | import org.eclipse.jgit.lib.Repository; |
| 53 | import org.eclipse.jgit.revwalk.RevTree; |
| 54 | import org.eclipse.jgit.revwalk.RevWalk; |
| 55 | import org.eclipse.jgit.treewalk.TreeWalk; |
| Shawn Pearce | 374f184 | 2015-02-10 15:36:54 -0800 | [diff] [blame] | 56 | |
| Shawn Pearce | 374f184 | 2015-02-10 15:36:54 -0800 | [diff] [blame] | 57 | public class DocServlet extends BaseServlet { |
| 58 | private static final long serialVersionUID = 1L; |
| 59 | |
| 60 | private static final String INDEX_MD = "index.md"; |
| 61 | private static final String NAVBAR_MD = "navbar.md"; |
| 62 | private static final String SOY_FILE = "Doc.soy"; |
| 63 | private static final String SOY_TEMPLATE = "gitiles.markdownDoc"; |
| 64 | |
| 65 | // Generation of ETag logic. Bump this only if DocServlet logic changes |
| 66 | // significantly enough to impact cached pages. Soy template and source |
| 67 | // files are automatically hashed as part of the ETag. |
| Shawn Pearce | 12c8fab | 2016-05-15 16:55:21 -0700 | [diff] [blame] | 68 | private static final int ETAG_GEN = 5; |
| Shawn Pearce | 374f184 | 2015-02-10 15:36:54 -0800 | [diff] [blame] | 69 | |
| Shawn Pearce | 3739fcd | 2017-06-30 15:38:17 -0700 | [diff] [blame] | 70 | private final HtmlSanitizer.Factory htmlSanitizer; |
| 71 | |
| Shawn Pearce | 374f184 | 2015-02-10 15:36:54 -0800 | [diff] [blame] | 72 | public DocServlet(GitilesAccess.Factory accessFactory, Renderer renderer) { |
| Shawn Pearce | 3739fcd | 2017-06-30 15:38:17 -0700 | [diff] [blame] | 73 | this(accessFactory, renderer, HtmlSanitizer.DISABLED_FACTORY); |
| 74 | } |
| 75 | |
| 76 | public DocServlet( |
| 77 | GitilesAccess.Factory accessFactory, Renderer renderer, HtmlSanitizer.Factory htmlSanitizer) { |
| Shawn Pearce | 374f184 | 2015-02-10 15:36:54 -0800 | [diff] [blame] | 78 | super(renderer, accessFactory); |
| Shawn Pearce | 3739fcd | 2017-06-30 15:38:17 -0700 | [diff] [blame] | 79 | this.htmlSanitizer = htmlSanitizer; |
| Shawn Pearce | 374f184 | 2015-02-10 15:36:54 -0800 | [diff] [blame] | 80 | } |
| 81 | |
| 82 | @Override |
| Han-Wen Nienhuys | c0200f6 | 2016-05-02 17:34:51 +0200 | [diff] [blame] | 83 | protected void doGetHtml(HttpServletRequest req, HttpServletResponse res) throws IOException { |
| Shawn Pearce | 47fd656 | 2016-05-28 14:15:15 -0700 | [diff] [blame] | 84 | MarkdownConfig cfg = MarkdownConfig.get(getAccess(req).getConfig()); |
| 85 | if (!cfg.render) { |
| Masaya Suzuki | 5cecb86 | 2019-03-25 17:35:44 -0700 | [diff] [blame] | 86 | throw new GitilesRequestFailureException(FailureReason.MARKDOWN_NOT_ENABLED); |
| Shawn Pearce | 374f184 | 2015-02-10 15:36:54 -0800 | [diff] [blame] | 87 | } |
| 88 | |
| 89 | GitilesView view = ViewFilter.getView(req); |
| 90 | Repository repo = ServletUtils.getRepository(req); |
| Shawn Pearce | b5ad0a0 | 2015-05-24 20:33:17 -0700 | [diff] [blame] | 91 | try (RevWalk rw = new RevWalk(repo)) { |
| Shawn Pearce | 47fd656 | 2016-05-28 14:15:15 -0700 | [diff] [blame] | 92 | ObjectReader reader = rw.getObjectReader(); |
| Shawn Pearce | 374f184 | 2015-02-10 15:36:54 -0800 | [diff] [blame] | 93 | String path = view.getPathPart(); |
| 94 | RevTree root; |
| 95 | try { |
| 96 | root = rw.parseTree(view.getRevision().getId()); |
| 97 | } catch (IncorrectObjectTypeException e) { |
| Masaya Suzuki | 5cecb86 | 2019-03-25 17:35:44 -0700 | [diff] [blame] | 98 | throw new GitilesRequestFailureException(FailureReason.INCORRECT_OBJECT_TYPE); |
| Shawn Pearce | 374f184 | 2015-02-10 15:36:54 -0800 | [diff] [blame] | 99 | } |
| 100 | |
| Shawn Pearce | 47fd656 | 2016-05-28 14:15:15 -0700 | [diff] [blame] | 101 | MarkdownFile srcmd = findFile(rw, root, path); |
| Shawn Pearce | 374f184 | 2015-02-10 15:36:54 -0800 | [diff] [blame] | 102 | if (srcmd == null) { |
| Masaya Suzuki | 5cecb86 | 2019-03-25 17:35:44 -0700 | [diff] [blame] | 103 | throw new GitilesRequestFailureException(FailureReason.OBJECT_NOT_FOUND); |
| Shawn Pearce | 374f184 | 2015-02-10 15:36:54 -0800 | [diff] [blame] | 104 | } |
| 105 | |
| Robert Liao | e8f7411 | 2018-10-24 18:10:51 -0700 | [diff] [blame] | 106 | MarkdownFile navmd = findNavbar(rw, root, path); |
| Shawn Pearce | 374f184 | 2015-02-10 15:36:54 -0800 | [diff] [blame] | 107 | String curEtag = etag(srcmd, navmd); |
| Shawn Pearce | 47fd656 | 2016-05-28 14:15:15 -0700 | [diff] [blame] | 108 | if (etagMatch(req, curEtag)) { |
| Shawn Pearce | 374f184 | 2015-02-10 15:36:54 -0800 | [diff] [blame] | 109 | res.setStatus(SC_NOT_MODIFIED); |
| 110 | return; |
| 111 | } |
| 112 | |
| 113 | view = view.toBuilder().setPathPart(srcmd.path).build(); |
| Shawn Pearce | 47fd656 | 2016-05-28 14:15:15 -0700 | [diff] [blame] | 114 | try { |
| 115 | srcmd.read(reader, cfg); |
| 116 | if (navmd != null) { |
| 117 | navmd.read(reader, cfg); |
| 118 | } |
| David Pursehouse | 6dec837 | 2019-05-13 06:51:32 +0200 | [diff] [blame^] | 119 | } catch (LargeObjectException.ExceedsLimit e) { |
| 120 | fileTooBig(res, view); |
| Shawn Pearce | 47fd656 | 2016-05-28 14:15:15 -0700 | [diff] [blame] | 121 | return; |
| Shawn Pearce | 374f184 | 2015-02-10 15:36:54 -0800 | [diff] [blame] | 122 | } |
| 123 | |
| Shawn Pearce | 47fd656 | 2016-05-28 14:15:15 -0700 | [diff] [blame] | 124 | MarkdownToHtml.Builder fmt = |
| 125 | MarkdownToHtml.builder() |
| 126 | .setConfig(cfg) |
| 127 | .setGitilesView(view) |
| Shawn Pearce | c68ad0b | 2016-05-28 16:52:47 -0700 | [diff] [blame] | 128 | .setRequestUri(req.getRequestURI()) |
| Shawn Pearce | 47fd656 | 2016-05-28 14:15:15 -0700 | [diff] [blame] | 129 | .setReader(reader) |
| Shawn Pearce | 3739fcd | 2017-06-30 15:38:17 -0700 | [diff] [blame] | 130 | .setRootTree(root) |
| 131 | .setHtmlSanitizer(htmlSanitizer.create(req)); |
| Shawn Pearce | f485a31 | 2017-07-01 12:10:27 -0700 | [diff] [blame] | 132 | Navbar navbar = createNavbar(cfg, fmt, navmd); |
| Shawn Pearce | 374f184 | 2015-02-10 15:36:54 -0800 | [diff] [blame] | 133 | res.setHeader(HttpHeaders.ETAG, curEtag); |
| Shawn Pearce | f485a31 | 2017-07-01 12:10:27 -0700 | [diff] [blame] | 134 | showDoc(req, res, view, fmt, navbar, srcmd); |
| Shawn Pearce | 374f184 | 2015-02-10 15:36:54 -0800 | [diff] [blame] | 135 | } |
| 136 | } |
| 137 | |
| Shawn Pearce | 47fd656 | 2016-05-28 14:15:15 -0700 | [diff] [blame] | 138 | private static boolean etagMatch(HttpServletRequest req, String etag) { |
| 139 | String reqEtag = req.getHeader(HttpHeaders.IF_NONE_MATCH); |
| 140 | return reqEtag != null && reqEtag.equals(etag); |
| 141 | } |
| 142 | |
| 143 | private String etag(MarkdownFile srcmd, @Nullable MarkdownFile navmd) { |
| Shawn Pearce | 374f184 | 2015-02-10 15:36:54 -0800 | [diff] [blame] | 144 | byte[] b = new byte[Constants.OBJECT_ID_LENGTH]; |
| David Pursehouse | d591449 | 2017-05-31 13:08:22 +0900 | [diff] [blame] | 145 | Hasher h = Hashing.murmur3_128().newHasher(); |
| Shawn Pearce | 374f184 | 2015-02-10 15:36:54 -0800 | [diff] [blame] | 146 | h.putInt(ETAG_GEN); |
| 147 | |
| 148 | renderer.getTemplateHash(SOY_FILE).writeBytesTo(b, 0, b.length); |
| 149 | h.putBytes(b); |
| 150 | |
| 151 | if (navmd != null) { |
| 152 | navmd.id.copyRawTo(b, 0); |
| 153 | h.putBytes(b); |
| 154 | } |
| 155 | |
| 156 | srcmd.id.copyRawTo(b, 0); |
| 157 | h.putBytes(b); |
| 158 | return h.hash().toString(); |
| 159 | } |
| 160 | |
| David Pursehouse | 3bce356 | 2018-11-08 16:23:38 +0900 | [diff] [blame] | 161 | private MarkdownFile findNavbar(RevWalk rw, RevTree root, String path) throws IOException { |
| Robert Liao | e8f7411 | 2018-10-24 18:10:51 -0700 | [diff] [blame] | 162 | if (!Strings.isNullOrEmpty(path)) { |
| 163 | // Traverse up the path until we find a NAVBAR_MD. |
| 164 | StringBuilder pathRemaining = new StringBuilder(path); |
| 165 | while (pathRemaining.length() > 0) { |
| 166 | int lastPathSeparatorIndex = pathRemaining.lastIndexOf("/"); |
| 167 | pathRemaining.setLength(lastPathSeparatorIndex + 1); |
| 168 | MarkdownFile navmd = findFile(rw, root, pathRemaining.toString() + NAVBAR_MD); |
| 169 | if (navmd != null) { |
| 170 | return navmd; |
| 171 | } |
| 172 | pathRemaining.setLength(Math.max(lastPathSeparatorIndex, 0)); |
| 173 | } |
| 174 | return null; |
| 175 | } |
| 176 | |
| 177 | return findFile(rw, root, NAVBAR_MD); |
| 178 | } |
| 179 | |
| Han-Wen Nienhuys | c0200f6 | 2016-05-02 17:34:51 +0200 | [diff] [blame] | 180 | private void showDoc( |
| 181 | HttpServletRequest req, |
| 182 | HttpServletResponse res, |
| 183 | GitilesView view, |
| Shawn Pearce | 47fd656 | 2016-05-28 14:15:15 -0700 | [diff] [blame] | 184 | MarkdownToHtml.Builder fmt, |
| Shawn Pearce | f485a31 | 2017-07-01 12:10:27 -0700 | [diff] [blame] | 185 | Navbar navbar, |
| Shawn Pearce | 47fd656 | 2016-05-28 14:15:15 -0700 | [diff] [blame] | 186 | MarkdownFile srcFile) |
| Han-Wen Nienhuys | c0200f6 | 2016-05-02 17:34:51 +0200 | [diff] [blame] | 187 | throws IOException { |
| Shawn Pearce | 374f184 | 2015-02-10 15:36:54 -0800 | [diff] [blame] | 188 | Map<String, Object> data = new HashMap<>(); |
| Shawn Pearce | f485a31 | 2017-07-01 12:10:27 -0700 | [diff] [blame] | 189 | data.putAll(navbar.toSoyData()); |
| Shawn Pearce | 12c8fab | 2016-05-15 16:55:21 -0700 | [diff] [blame] | 190 | |
| Shawn Pearce | f485a31 | 2017-07-01 12:10:27 -0700 | [diff] [blame] | 191 | MarkdownConfig cfg = navbar.getConfig(); |
| Shawn Pearce | b55cf2b | 2017-06-29 21:56:37 -0700 | [diff] [blame] | 192 | Node doc = GitilesMarkdown.parse(cfg, srcFile.consumeContent()); |
| Shawn Pearce | 47fd656 | 2016-05-28 14:15:15 -0700 | [diff] [blame] | 193 | data.put("pageTitle", pageTitle(doc, srcFile)); |
| Shawn Pearce | 68311c7 | 2015-06-09 17:01:34 -0700 | [diff] [blame] | 194 | if (view.getType() != GitilesView.Type.ROOTED_DOC) { |
| 195 | data.put("sourceUrl", GitilesView.show().copyFrom(view).toUrl()); |
| 196 | data.put("logUrl", GitilesView.log().copyFrom(view).toUrl()); |
| 197 | data.put("blameUrl", GitilesView.blame().copyFrom(view).toUrl()); |
| 198 | } |
| Shawn Pearce | 47fd656 | 2016-05-28 14:15:15 -0700 | [diff] [blame] | 199 | if (cfg.analyticsId != null) { |
| 200 | data.put("analyticsId", cfg.analyticsId); |
| Shawn Pearce | bc381a4 | 2015-06-22 12:17:43 -0700 | [diff] [blame] | 201 | } |
| 202 | |
| Shawn Pearce | 5c34e09 | 2017-06-29 21:18:30 -0700 | [diff] [blame] | 203 | try (OutputStream out = startRenderCompressedStreamingHtml(req, res, SOY_TEMPLATE, data)) { |
| 204 | Writer w = newWriter(out, res); |
| Shawn Pearce | f485a31 | 2017-07-01 12:10:27 -0700 | [diff] [blame] | 205 | fmt.setConfig(cfg) |
| 206 | .setFilePath(srcFile.path) |
| 207 | .build() |
| 208 | .renderToHtml(new StreamHtmlBuilder(w), doc); |
| Shawn Pearce | 5c34e09 | 2017-06-29 21:18:30 -0700 | [diff] [blame] | 209 | w.flush(); |
| 210 | } catch (RuntimeIOException e) { |
| 211 | Throwables.throwIfInstanceOf(e.getCause(), IOException.class); |
| 212 | throw e; |
| Shawn Pearce | 374f184 | 2015-02-10 15:36:54 -0800 | [diff] [blame] | 213 | } |
| Shawn Pearce | 5c34e09 | 2017-06-29 21:18:30 -0700 | [diff] [blame] | 214 | } |
| 215 | |
| Shawn Pearce | f485a31 | 2017-07-01 12:10:27 -0700 | [diff] [blame] | 216 | private static Navbar createNavbar( |
| 217 | MarkdownConfig cfg, MarkdownToHtml.Builder fmt, @Nullable MarkdownFile navFile) { |
| 218 | Navbar navbar = new Navbar().setConfig(cfg); |
| Shawn Pearce | 5c34e09 | 2017-06-29 21:18:30 -0700 | [diff] [blame] | 219 | if (navFile != null) { |
| Shawn Pearce | f485a31 | 2017-07-01 12:10:27 -0700 | [diff] [blame] | 220 | navbar |
| 221 | .setFormatter(fmt.setFilePath(navFile.path).build()) |
| 222 | .setMarkdown(navFile.consumeContent()); |
| Shawn Pearce | 5c34e09 | 2017-06-29 21:18:30 -0700 | [diff] [blame] | 223 | } |
| Shawn Pearce | f485a31 | 2017-07-01 12:10:27 -0700 | [diff] [blame] | 224 | return navbar; |
| Shawn Pearce | 374f184 | 2015-02-10 15:36:54 -0800 | [diff] [blame] | 225 | } |
| 226 | |
| Shawn Pearce | 47fd656 | 2016-05-28 14:15:15 -0700 | [diff] [blame] | 227 | private static String pageTitle(Node doc, MarkdownFile srcFile) { |
| 228 | String title = MarkdownUtil.getTitle(doc); |
| 229 | return MoreObjects.firstNonNull(title, srcFile.path); |
| 230 | } |
| 231 | |
| 232 | @Nullable |
| 233 | private static MarkdownFile findFile(RevWalk rw, RevTree root, String path) throws IOException { |
| Shawn Pearce | 374f184 | 2015-02-10 15:36:54 -0800 | [diff] [blame] | 234 | if (Strings.isNullOrEmpty(path)) { |
| 235 | path = INDEX_MD; |
| 236 | } |
| 237 | |
| 238 | ObjectReader reader = rw.getObjectReader(); |
| David Pursehouse | c3e772a | 2016-06-15 21:49:35 +0900 | [diff] [blame] | 239 | try (TreeWalk tw = TreeWalk.forPath(reader, path, root)) { |
| 240 | if (tw == null) { |
| 241 | return null; |
| 242 | } |
| 243 | if ((tw.getRawMode(0) & TYPE_MASK) == TYPE_TREE) { |
| 244 | if (findIndexFile(tw)) { |
| 245 | path = tw.getPathString(); |
| 246 | } else { |
| 247 | return null; |
| 248 | } |
| 249 | } |
| 250 | if ((tw.getRawMode(0) & TYPE_MASK) == TYPE_FILE) { |
| 251 | if (!path.endsWith(".md")) { |
| 252 | return null; |
| 253 | } |
| 254 | return new MarkdownFile(path, tw.getObjectId(0)); |
| 255 | } |
| Shawn Pearce | 374f184 | 2015-02-10 15:36:54 -0800 | [diff] [blame] | 256 | return null; |
| 257 | } |
| Shawn Pearce | 374f184 | 2015-02-10 15:36:54 -0800 | [diff] [blame] | 258 | } |
| 259 | |
| 260 | private static boolean findIndexFile(TreeWalk tw) throws IOException { |
| 261 | tw.enterSubtree(); |
| 262 | while (tw.next()) { |
| Han-Wen Nienhuys | c0200f6 | 2016-05-02 17:34:51 +0200 | [diff] [blame] | 263 | if ((tw.getRawMode(0) & TYPE_MASK) == TYPE_FILE && INDEX_MD.equals(tw.getNameString())) { |
| Shawn Pearce | 374f184 | 2015-02-10 15:36:54 -0800 | [diff] [blame] | 264 | return true; |
| 265 | } |
| 266 | } |
| 267 | return false; |
| 268 | } |
| 269 | |
| David Pursehouse | 6dec837 | 2019-05-13 06:51:32 +0200 | [diff] [blame^] | 270 | private static void fileTooBig(HttpServletResponse res, GitilesView view) throws IOException { |
| Shawn Pearce | 47fd656 | 2016-05-28 14:15:15 -0700 | [diff] [blame] | 271 | if (view.getType() == GitilesView.Type.ROOTED_DOC) { |
| Masaya Suzuki | 5cecb86 | 2019-03-25 17:35:44 -0700 | [diff] [blame] | 272 | throw new GitilesRequestFailureException(FailureReason.OBJECT_TOO_LARGE); |
| Shawn Pearce | 47fd656 | 2016-05-28 14:15:15 -0700 | [diff] [blame] | 273 | } else { |
| 274 | res.sendRedirect(GitilesView.show().copyFrom(view).toUrl()); |
| 275 | } |
| 276 | } |
| 277 | |
| Shawn Pearce | 47fd656 | 2016-05-28 14:15:15 -0700 | [diff] [blame] | 278 | private static class MarkdownFile { |
| Shawn Pearce | 374f184 | 2015-02-10 15:36:54 -0800 | [diff] [blame] | 279 | final String path; |
| 280 | final ObjectId id; |
| Shawn Pearce | 47fd656 | 2016-05-28 14:15:15 -0700 | [diff] [blame] | 281 | byte[] content; |
| Shawn Pearce | 374f184 | 2015-02-10 15:36:54 -0800 | [diff] [blame] | 282 | |
| Shawn Pearce | 47fd656 | 2016-05-28 14:15:15 -0700 | [diff] [blame] | 283 | MarkdownFile(String path, ObjectId id) { |
| Shawn Pearce | 374f184 | 2015-02-10 15:36:54 -0800 | [diff] [blame] | 284 | this.path = path; |
| 285 | this.id = id; |
| 286 | } |
| 287 | |
| Shawn Pearce | 47fd656 | 2016-05-28 14:15:15 -0700 | [diff] [blame] | 288 | void read(ObjectReader reader, MarkdownConfig cfg) throws IOException { |
| 289 | content = reader.open(id, OBJ_BLOB).getCachedBytes(cfg.inputLimit); |
| Shawn Pearce | 374f184 | 2015-02-10 15:36:54 -0800 | [diff] [blame] | 290 | } |
| Shawn Pearce | 5c34e09 | 2017-06-29 21:18:30 -0700 | [diff] [blame] | 291 | |
| 292 | byte[] consumeContent() { |
| 293 | byte[] c = content; |
| 294 | content = null; |
| 295 | return c; |
| 296 | } |
| Shawn Pearce | 374f184 | 2015-02-10 15:36:54 -0800 | [diff] [blame] | 297 | } |
| 298 | } |