All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.netbeans.upgrade.CopyFiles Maven / Gradle / Ivy

The newest version!
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.
 */
package org.netbeans.upgrade;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.Reader;
import java.nio.file.Files;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map.Entry;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JDialog;
import javax.swing.JOptionPane;
import org.netbeans.util.Util;
import org.openide.util.EditableProperties;

import static java.nio.charset.StandardCharsets.UTF_8;

/** Does copy of files according to include/exclude patterns.
 *
 * @author Jiri Skrivanek
 */
final class CopyFiles {

    private File sourceRoot;
    private File targetRoot;
    private EditableProperties currentProperties;
    private Set includePatterns = new HashSet<>();
    private Set excludePatterns = new HashSet<>();
    private HashMap translatePatterns = new HashMap<>(); // 
    private static final Logger LOGGER = Logger.getLogger(CopyFiles.class.getName());

    private CopyFiles(File source, File target, File patternsFile) {
        this.sourceRoot = source;
        this.targetRoot = target;
        try {
            try (Reader reader = new InputStreamReader(new FileInputStream(patternsFile), UTF_8))  {// NOI18N
                readPatterns(reader);
            }
        } catch (IOException ex) {
            // set these to null to stop further copying (see copyDeep method)
            sourceRoot = null;
            targetRoot = null;
            LOGGER.log(Level.WARNING, "Import settings will not proceed: {0}", ex.getMessage());
            // show error message and continue
            JDialog dialog = Util.createJOptionDialog(new JOptionPane(ex, JOptionPane.ERROR_MESSAGE), "Import settings will not proceed");
            dialog.setVisible(true);
        }
    }

    public static void copyDeep(File source, File target, File patternsFile) throws IOException {
        CopyFiles copyFiles = new CopyFiles(source, target, patternsFile);
        if(copyFiles.sourceRoot == null && copyFiles.targetRoot == null) {
            return; // IOException was thrown in CopyFiles constructor, probably netbeans.import could not be located
        }
        LOGGER.fine("Copying from: " + copyFiles.sourceRoot + "\nto: " + copyFiles.targetRoot);  //NOI18N
        copyFiles.copyFolder(copyFiles.sourceRoot);
    }

    private void copyFolder(File sourceFolder) throws IOException {
        File[] srcChildren = sourceFolder.listFiles();
        if (srcChildren == null) {
            LOGGER.info(sourceFolder + " is not a directory or is invalid.");  //NOI18N
            return ;
        }
        for (File child : srcChildren) {
            if (child.isDirectory()) {
                copyFolder(child);
            } else {
                copyFile(child);
            }
        }
    }

    /** Returns slash separated path relative to given root. */
    private static String getRelativePath(File root, File file) {
        String result = file.getAbsolutePath().substring(root.getAbsolutePath().length());
        result = result.replace('\\', '/');  //NOI18N
        if (result.startsWith("/") && !result.startsWith("//")) {  //NOI18N
            result = result.substring(1);
        }
        return result;
    }

    /** Copy source file to target file. It creates necessary sub folders.
     * @param sourceFile source file
     * @param targetFile target file
     * @throws java.io.IOException if copying fails
     */
    private static void copyFile(File sourceFile, File targetFile) throws IOException {
        ensureParent(targetFile);
        Files.copy(sourceFile.toPath(), targetFile.toPath());
    }

    /** Copy given file to target root dir if matches include/exclude patterns.
     * If properties pattern is applicable, it copies only matching keys.
     * @param sourceFile source file
     * @throws java.io.IOException if copying fails
     */
    private void copyFile(File sourceFile) throws IOException {

        // invalid code point check JDK-8075156
        if (sourceFile.getName().endsWith(".properties")
                && new String(Files.readAllBytes(sourceFile.toPath()), UTF_8).indexOf('\u0000') != -1) {
            LOGGER.log(Level.WARNING, "{0} contains invalid code points -> skipping", sourceFile);  //NOI18N
            return;
        }

        String relativePath = getRelativePath(sourceRoot, sourceFile);
        currentProperties = null;
        boolean includeFile = false;
        Set includeKeys = new HashSet<>();
        Set excludeKeys = new HashSet<>();
        for (String pattern : includePatterns) {
            if (pattern.contains("#")) {  //NOI18N
                includeKeys.addAll(matchingKeys(relativePath, pattern));
            } else {
                if (relativePath.matches(pattern)) {
                    includeFile = true;
                    includeKeys.clear();  // include entire file
                    break;
                }
            }
        }
        if (includeFile || !includeKeys.isEmpty()) {
            // check excludes
            for (String pattern : excludePatterns) {
                if (pattern.contains("#")) {  //NOI18N
                    excludeKeys.addAll(matchingKeys(relativePath, pattern));
                } else {
                    if (relativePath.matches(pattern)) {
                        includeFile = false;
                        includeKeys.clear();  // exclude entire file
                        break;
                    }
                }
            }
        }
        LOGGER.log(Level.FINEST, "{0}, includeFile={1}, includeKeys={2}, excludeKeys={3}", new Object[]{relativePath, includeFile, includeKeys, excludeKeys});  //NOI18N
        if (!includeFile && includeKeys.isEmpty()) {
            // nothing matches
            return;
        }
        
	for (Entry entry : translatePatterns.entrySet()) {
	    if (relativePath.startsWith(entry.getKey())) {
		String value = entry.getValue();
		LOGGER.log(Level.INFO, "Translating old relative path: {0}", relativePath);  //NOI18N
		relativePath = value + relativePath.substring(entry.getKey().length());
		LOGGER.log(Level.INFO, "                   to new one: {0}", relativePath);  //NOI18N
	    }
	}

        File targetFile = new File(targetRoot, relativePath);
        LOGGER.log(Level.FINE, "Path: {0}", relativePath);  //NOI18N
        if (includeKeys.isEmpty() && excludeKeys.isEmpty()) {
            // copy entire file
            copyFile(sourceFile, targetFile);
        } else {
            if (!includeKeys.isEmpty()) {
                currentProperties.keySet().retainAll(includeKeys);
            }
            currentProperties.keySet().removeAll(excludeKeys);
            // copy just selected keys
            LOGGER.log(Level.FINE, "  Only keys: {0}", currentProperties.keySet());
            ensureParent(targetFile);
            try (OutputStream out = new FileOutputStream(targetFile)) {
                currentProperties.store(out);
            }
        }
    }

    /** Returns set of keys matching given pattern.
     * @param relativePath path relative to sourceRoot
     * @param propertiesPattern pattern like file.properties#keyPattern
     * @return set of matching keys, never null
     * @throws IOException if properties cannot be loaded
     */
    private Set matchingKeys(String relativePath, String propertiesPattern) throws IOException {
        Set matchingKeys = new HashSet<>();
        String[] patterns = propertiesPattern.split("#", 2);
        String filePattern = patterns[0];
        String keyPattern = patterns[1];
        if (relativePath.matches(filePattern)) {
            if (currentProperties == null) {
                currentProperties = getProperties(relativePath);
            }
            for (String key : currentProperties.keySet()) {
                if (key.matches(keyPattern)) {
                    matchingKeys.add(key);
                }
            }
        }
        return matchingKeys;
    }

    /** Returns properties from relative path.
     * @param relativePath relative path
     * @return properties from relative path.
     * @throws IOException if cannot open stream
     */
    private EditableProperties getProperties(String relativePath) throws IOException {
        EditableProperties properties = new EditableProperties(false);
        try (InputStream in = new FileInputStream(new File(sourceRoot, relativePath))) {
            properties.load(in);
        }
        return properties;
    }

    /** Creates parent of given file, if doesn't exist. */
    private static void ensureParent(File file) throws IOException {
        final File parent = file.getParentFile();
        if (parent != null && !parent.exists()) {
            if (!parent.mkdirs()) {
                throw new IOException("Cannot create folder: " + parent.getAbsolutePath());  //NOI18N
            }
        }
    }

    /** Reads the include/exclude set from a given reader.
     * @param r reader
     */
    private void readPatterns(Reader r) throws IOException {
        BufferedReader buf = new BufferedReader(r);
        for (;;) {
            String line = buf.readLine();
            if (line == null) {
                break;
            }
            line = line.strip();
            if (line.length() == 0 || line.startsWith("#")) {  //NOI18N
                continue;
            }
            if (line.startsWith("include ")) {  //NOI18N
                line = line.substring(8);
                if (line.length() > 0) {
                    includePatterns.addAll(parsePattern(line));
                }
            } else if (line.startsWith("exclude ")) {  //NOI18N
                line = line.substring(8);
                if (line.length() > 0) {
                    excludePatterns.addAll(parsePattern(line));
                }
            } else if (line.startsWith("translate ")) {  //NOI18N
                line = line.substring(10);
                if (line.length() > 0) {
		    String[] translations = line.split("\\|");
		    for (String translation : translations) {
			String originalPath = translation.substring(0, translation.indexOf("=>"));
			String newPath = translation.substring(translation.lastIndexOf("=>") + 2);
			if (translatePatterns.containsKey(originalPath)) {
			    LOGGER.log(Level.INFO, "Translation already exists: {0}. Ignoring new translation: {1}",  //NOI18N
				    new Object[]{originalPath.concat("=>").concat(translatePatterns.get(originalPath)),
					originalPath.concat("=>").concat(newPath)});
			} else {
			    translatePatterns.put(originalPath, newPath);
			}
		    }
                }
            } else {
                throw new java.io.IOException("Wrong line: " + line);  //NOI18N
            }
        }
    }

    enum ParserState {

        START,
        IN_KEY_PATTERN,
        AFTER_KEY_PATTERN,
        IN_BLOCK
    }

    /** Parses given compound string pattern into set of single patterns.
     * @param pattern compound pattern in form filePattern1#keyPattern1#|filePattern2#keyPattern2#|filePattern3
     * @return set of single patterns containing just one # (e.g. [filePattern1#keyPattern1, filePattern2#keyPattern2, filePattern3])
     */
    private static Set parsePattern(String pattern) {
        Set patterns = new HashSet<>();
        if (pattern.contains("#")) {  //NOI18N
            StringBuilder partPattern = new StringBuilder();
            ParserState state = ParserState.START;
            int blockLevel = 0;
            for (int i = 0; i < pattern.length(); i++) {
                char c = pattern.charAt(i);
                switch (state) {
                    case START:
                        if (c == '#') {
                            state = ParserState.IN_KEY_PATTERN;
                            partPattern.append(c);
                        } else if (c == '(') {
                            state = ParserState.IN_BLOCK;
                            blockLevel++;
                            partPattern.append(c);
                        } else if (c == '|') {
                            patterns.add(partPattern.toString());
                            partPattern = new StringBuilder();
                        } else {
                            partPattern.append(c);
                        }
                        break;
                    case IN_KEY_PATTERN:
                        if (c == '#') {
                            state = ParserState.AFTER_KEY_PATTERN;
                        } else {
                            partPattern.append(c);
                        }
                        break;
                    case AFTER_KEY_PATTERN:
                        if (c == '|') {
                            state = ParserState.START;
                            patterns.add(partPattern.toString());
                            partPattern = new StringBuilder();
                        } else {
                            assert false : "Wrong OptionsExport pattern " + pattern + ". Only format like filePattern1#keyPattern#|filePattern2 is supported.";  //NOI18N
                        }
                        break;
                    case IN_BLOCK:
                        partPattern.append(c);
                        if (c == ')') {
                            blockLevel--;
                            if (blockLevel == 0) {
                                state = ParserState.START;
                            }
                        }
                        break;
                }
            }
            patterns.add(partPattern.toString());
        } else {
            patterns.add(pattern);
        }
        return patterns;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy