/* * Copyright (C) 2018-2021, Andre Bossert * * This program and the accompanying materials are made available under the * terms of the Eclipse Distribution License v. 1.0 which is available at * https://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: BSD-3-Clause */ package org.eclipse.jgit.internal.diffmergetool; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.Map; import org.eclipse.jgit.diff.DiffEntry; /** * The element used as left or right file for compare. * */ public class FileElement { /** * The file element type. */ public enum Type { /** * The local file element (ours). */ LOCAL, /** * The remote file element (theirs). */ REMOTE, /** * The merged file element (path in worktree). */ MERGED, /** * The base file element (of ours and theirs). */ BASE, /** * The backup file element (copy of merged / conflicted). */ BACKUP } private final String path; private final Type type; private final File workDir; private InputStream stream; private File tempFile; /** * Creates file element for path. * * @param path * the file path * @param type * the element type */ public FileElement(String path, Type type) { this(path, type, null); } /** * Creates file element for path. * * @param path * the file path * @param type * the element type * @param workDir * the working directory of the path (can be null, then current * working dir is used) */ public FileElement(String path, Type type, File workDir) { this(path, type, workDir, null); } /** * Create file element * * @param path * the file path * @param type * the element type * @param workDir * the working directory of the path (can be null, then current * working dir is used) * @param stream * the object stream to load and write on demand, @see getFile(), * to tempFile once (can be null) */ public FileElement(String path, Type type, File workDir, InputStream stream) { this.path = path; this.type = type; this.workDir = workDir; this.stream = stream; } /** * Get path * * @return the file path */ public String getPath() { return path; } /** * Get type * * @return the element type */ public Type getType() { return type; } /** * Get file *

* Return *

* * @return the object stream * @throws IOException * if an IO error occurred */ public File getFile() throws IOException { // if we have already temp file and no stream // then just return this temp file (it was filled from outside) if ((tempFile != null) && (stream == null)) { return tempFile; } File file = new File(workDir, path); // if we have a stream or file is missing (path is "/dev/null") // then optionally create temporary file and fill it with stream content if ((stream != null) || isNullPath()) { if (tempFile == null) { tempFile = getTempFile(file, type.name(), null); } if (stream != null) { copyFromStream(tempFile, stream); } // invalidate the stream, because it is used once stream = null; return tempFile; } return file; } /** * Check if path id "/dev/null" * * @return true if path is "/dev/null" */ public boolean isNullPath() { return path.equals(DiffEntry.DEV_NULL); } /** * Create temporary file in given or system temporary directory. * * @param directory * the directory for the file (can be null); if null system * temporary directory is used * @return temporary file in directory or in the system temporary directory * @throws IOException * if an IO error occurred */ public File createTempFile(File directory) throws IOException { if (tempFile == null) { tempFile = getTempFile(new File(path), type.name(), directory); } return tempFile; } /** * Delete and invalidate temporary file if necessary. */ public void cleanTemporaries() { if (tempFile != null && tempFile.exists()) { tempFile.delete(); } tempFile = null; } /** * Replace variable in input. * * @param input * the input string * @return the replaced input string * @throws IOException * if an IO error occurred */ public String replaceVariable(String input) throws IOException { return input.replace("$" + type.name(), getFile().getPath()); //$NON-NLS-1$ } /** * Add variable to environment map. * * @param env * the environment where this element should be added * @throws IOException * if an IO error occurred */ public void addToEnv(Map env) throws IOException { env.put(type.name(), getFile().getPath()); } private static File getTempFile(final File file, final String midName, final File workingDir) throws IOException { String[] fileNameAndExtension = splitBaseFileNameAndExtension(file); // TODO: avoid long random file name (number generated by // createTempFile) return File.createTempFile( fileNameAndExtension[0] + "_" + midName + "_", //$NON-NLS-1$ //$NON-NLS-2$ fileNameAndExtension[1], workingDir); } private static void copyFromStream(final File file, final InputStream stream) throws IOException, FileNotFoundException { try (OutputStream outStream = new FileOutputStream(file)) { int read = 0; byte[] bytes = new byte[8 * 1024]; while ((read = stream.read(bytes)) != -1) { outStream.write(bytes, 0, read); } } finally { // stream can only be consumed once --> close it and invalidate stream.close(); } } private static String[] splitBaseFileNameAndExtension(File file) { String[] result = new String[2]; result[0] = file.getName(); result[1] = ""; //$NON-NLS-1$ int idx = result[0].lastIndexOf("."); //$NON-NLS-1$ // if "." was found (>-1) and last-index is not first char (>0), then // split (same behavior like cgit) if (idx > 0) { result[1] = result[0].substring(idx, result[0].length()); result[0] = result[0].substring(0, idx); } return result; } }