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

org.xmlvm.util.FileMerger Maven / Gradle / Ivy

There is a newer version: 0.96-beta4
Show newest version
/* Copyright (c) 2002-2011 by XMLVM.org
 *
 * Project Info:  http://www.xmlvm.org
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
 * USA.
 */

package org.xmlvm.util;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import org.xmlvm.Log;
import org.xmlvm.proc.out.OutputFile;
import org.xmlvm.util.universalfile.UniversalFile;

/**
 * This is a utility function to merge two files. It is not a traditional
 * merging tool, though. Instead it uses files that have section which are
 * described by special XMLVM markup.
 * 

* Sections start with {@code //XMLVM_BEGIN} and end with {@code //XMLVM_END}. * These markers have to be the first non-whitespace characters on the line. * They can be followed by an arbitrary string, which is ignored for the end * marker. For the begin marker though this trailing string is a key for this * section. Sections must have unique keys within one file. *

* This utility takes file pairs which are merged like this: All sections are * extracted from the source and destination file and stored with their key. If * a section is contained in both source- and destination file then the contents * of the section in the source file replace the contents of that same section * in the destination file. */ public class FileMerger { private static final String TAG= FileMerger.class.getSimpleName(); private static final String BEGIN_MARKER= "//XMLVM_BEGIN"; private static final String END_MARKER= "//XMLVM_END"; private final Collection skeletons; private final String skeletonBasePath; private final Collection implementations; private final String implementationBasePath; /** * Instantiates a new FileMerger. * * @param skeletons * These are the files that contain the general structure of the * new files. Their content will be enriched with matching * implementations. * @param skeletonBasePath * The base path in which the skeleton files are (or will be). * This is important so that the relative path can be used to * correctly as the key to match implementation files. * @param implementations * These are the files that contain manual implementations that * should be preserved for all sections that are contained in the * matching wrapper file. * @param implementationBasePath * The path in which the implementation files are in. This is * important so that the relative path can be used to match * skeleton files. */ public FileMerger(Collection skeletons, String skeletonBasePath, Collection implementations, String implementationBasePath) { this.skeletons= skeletons; this.skeletonBasePath= skeletonBasePath; this.implementations= implementations; this.implementationBasePath= implementationBasePath; } /** * Instantiates a new FileMerger. Use this constructor if relatives paths * should not be included in the matching of files. Only the name of the * file will be matched. A typical use case for this is when all skeleton * files are in only one directory and the implementation files are in one * directory without sub-directories as well. * * @param skeletons * These are the files that contain the general structure of the * new files. Their content will be enriched with matching * implementations. * @param implementations * These are the files that contain manual implementations that * should be preserved for all sections that are contained in the * matching wrapper file. */ public FileMerger(Collection skeletons, Collection implementations) { this.skeletons= skeletons; this.skeletonBasePath= null; this.implementations= implementations; this.implementationBasePath= null; } public void process() { // Extracts all existing sections from all files and keys them by // relative file name. Map> existingSections= extractAllSections(); // We patch the existing content into the skeletons. injectAllSections(existingSections); } /** * From the given destination directory, this method extracts all blocks * that are enclosed within XMLVM_BEGIN and XMLVM_END blocks. It keys them * first by file name and then by section name. * * @param implementations * The files that contain implementations that should be * preserved. * @param basePath * The base path of the implementation files. This is used to * calculate the relative path of the files as a key. * @return The section contents keyed by filename and section name. */ private Map> extractAllSections() { Map> output= new HashMap>(); for (UniversalFile existingFile : implementations) { if (existingFile.isFile() && !existingFile.isEmpty()) { String key= implementationBasePath != null ? existingFile.getRelativePath(implementationBasePath) : existingFile.getName(); Map sections= extractSections(existingFile); output.put(key, sections); } } return output; } /** * Extracts the sections that are enclosed within XMLVM_BEGIN and XMLVM_END * blocks from the given file. * * @param file * the file to parse * @return A map that contains the contents of the blocks, keyed by block * name */ private Map extractSections(UniversalFile file) { Map sections= new HashMap(); BufferedReader reader= new BufferedReader(new StringReader(file.getFileAsString())); String line; StringBuilder section= null; String currentKey= null; try { while ((line= reader.readLine()) != null) { if (line.contains(BEGIN_MARKER)) { section= new StringBuilder(); currentKey= line.trim(); continue; } if (line.contains(END_MARKER)) { if (section == null) { Log.error(TAG, "Found end marker without matching starting marker: " + line); continue; } sections.put(currentKey, section.toString()); section= null; } if (section != null) { section.append(line); section.append('\n'); } } } catch (IOException e) { Log.error(TAG, "Could not read file: " + e.getMessage()); } return sections; } /** * Injects the given sections to the given files, if their relative file * name matches and they contain matching sections. * * @param sections * the previously extracted section contents which are keyed by * relative file name and section name */ private void injectAllSections(Map> sections) { for (OutputFile file : skeletons) { String key= skeletonBasePath != null ? file.getRelativePath(skeletonBasePath) : file.getFileName(); if (sections.containsKey(key)) { Log.debug(TAG, "Injecting contents into " + file.getFullPath()); injectSections(sections.get(key), file); } } } /** * Injects the given section contents, keyed by section name, into the given * file. If the file has sections that match the ones in the given map, * their content is replaced. * * @param sections * the section contents, keyed by section name * @param file * The file to parse and possibly replace the section contents * in. */ private void injectSections(Map sections, OutputFile file) { BufferedReader reader= new BufferedReader(new StringReader(file.getDataAsString())); try { StringBuilder fileContent= new StringBuilder(); String line; boolean doNotAdd= false; while ((line= reader.readLine()) != null) { if (!doNotAdd) { fileContent.append(line); fileContent.append('\n'); } if (line.contains(BEGIN_MARKER)) { String sectionKey= line.trim(); if (sections.containsKey(sectionKey)) { fileContent.append(sections.get(sectionKey)); doNotAdd= true; } } if (line.contains(END_MARKER)) { if (doNotAdd) { fileContent.append(line); fileContent.append('\n'); } doNotAdd= false; } } file.setData(fileContent.toString()); } catch (IOException e) { Log.error(TAG, "Could not read file: " + e.getMessage()); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy