net.sourceforge.docfetcher.model.UtilModel.java Source code

Java tutorial

Introduction

Here is the source code for net.sourceforge.docfetcher.model.UtilModel.java

Source

/*******************************************************************************
 * Copyright (c) 2011 Tran Nam Quang.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    Tran Nam Quang - initial API and implementation
 *******************************************************************************/

package net.sourceforge.docfetcher.model;

import static org.junit.Assert.assertEquals;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import net.sourceforge.docfetcher.model.index.DiskSpaceException;
import net.sourceforge.docfetcher.model.index.IndexingConfig;
import net.sourceforge.docfetcher.model.index.IndexingError;
import net.sourceforge.docfetcher.model.index.IndexingError.ErrorType;
import net.sourceforge.docfetcher.model.index.IndexingException;
import net.sourceforge.docfetcher.model.index.IndexingReporter;
import net.sourceforge.docfetcher.util.Util;
import net.sourceforge.docfetcher.util.annotations.ImmutableCopy;
import net.sourceforge.docfetcher.util.annotations.NotNull;
import net.sourceforge.docfetcher.util.annotations.Nullable;
import net.sourceforge.docfetcher.util.annotations.VisibleForPackageGroup;

import org.apache.lucene.index.IndexReader;
import org.apache.lucene.queryParser.QueryParser;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.Directory;

import com.google.common.base.Objects;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.io.Closeables;

import de.schlichtherle.truezip.file.TArchiveDetector;
import de.schlichtherle.truezip.file.TFile;

/**
 * @author Tran Nam Quang
 */
@VisibleForPackageGroup
public final class UtilModel {

    private UtilModel() {
    }

    // TODO pre-release: Can any of these methods be replaced by more efficient
    // Guava or Commons Lang 3 functionality?

    // TODO pre-release: Are any of these methods only used locally? If so,
    // move them to the call site.

    /**
     * Returns an immutable list containing the values of the given map, or an
     * immutable empty list if null was given.
     */
    @ImmutableCopy
    @NotNull
    static <K, V> List<V> nullSafeImmutableList(@Nullable Map<K, V> map) {
        if (map == null || map.isEmpty())
            return Collections.emptyList();
        return ImmutableList.copyOf(map.values());
    }

    /**
     * Returns an immutable copy of the given map, or an immutable empty map if
     * null was given.
     */
    @ImmutableCopy
    @NotNull
    static <K, V> Map<K, V> nullSafeImmutableMap(@Nullable Map<K, V> map) {
        if (map == null || map.isEmpty())
            return Collections.emptyMap();
        return ImmutableMap.copyOf(map);
    }

    @NotNull
    public static String getRelativePathIfPossible(@NotNull File file) {
        if (Util.isUncPath(file))
            return file.getPath();
        String absPath = Util.getAbsPath(file);
        assert Util.USER_DIR.isAbsolute();
        if (absPath.equals(Util.USER_DIR_PATH))
            return "";
        if (Util.IS_WINDOWS) {
            String d1 = Util.getDriveLetter(Util.USER_DIR_PATH);
            String d2 = Util.getDriveLetter(absPath);
            if (!d1.equals(d2))
                return absPath;
        }
        return UtilModel.getRelativePath(Util.USER_DIR_PATH, absPath);
    }

    /**
     * Returns a relative path that allows navigation from the absolute source
     * path {@code srcPath} to the absolute destination path {@code dstPath}.
     * Example outputs:
     * 
     * <pre>
     * ../..
     * ../../path/to/adjacent/file
     * path/to/subfolder
     * </pre>
     * 
     * The returned paths always use "/" as path separator, and there are no
     * leading or trailing path separators.
     */
    @NotNull
    @VisibleForPackageGroup
    public static String getRelativePath(@NotNull String srcPath, @NotNull String dstPath) {
        Iterator<String> srcIt = Util.splitPath(srcPath).iterator();
        Iterator<String> dstIt = Util.splitPath(dstPath).iterator();
        List<String> srcOverhang = new ArrayList<String>();
        List<String> dstOverhang = new ArrayList<String>();
        List<String> dstList = new ArrayList<String>();
        boolean stillEqual = true;
        while (true) {
            String srcPart = null;
            String dstPart = null;
            if (srcIt.hasNext())
                srcPart = srcIt.next();
            if (dstIt.hasNext()) {
                dstPart = dstIt.next();
                dstList.add(dstPart);
            }
            if (srcPart == null && dstPart == null)
                break;
            if (stillEqual && !Objects.equal(srcPart, dstPart))
                stillEqual = false;
            if (stillEqual)
                continue;
            if (srcPart != null)
                srcOverhang.add("..");
            if (dstPart != null)
                dstOverhang.add(dstPart);
        }
        String path = null;
        if (srcOverhang.isEmpty()) {
            if (dstOverhang.isEmpty())
                path = Util.joinPath(dstList);
            else
                path = Util.joinPath(dstOverhang);
        } else if (dstOverhang.isEmpty())
            path = Util.joinPath(srcOverhang);
        else
            path = Util.joinPath(srcOverhang) + "/" + Util.joinPath(dstOverhang);
        return path;
    }

    /**
     * Returns a relative path that allows navigation from the absolute path of
     * the source file {@code srcFile} to the absolute path of the destination
     * file {@code dstFile}. Example outputs:
     * 
     * <pre>
     * ../..
     * ../../path/to/adjacent/file
     * path/to/subfolder
     * </pre>
     * 
     * The returned paths always use "/" as path separator, and there are no
     * leading or trailing path separators.
     */
    @NotNull
    @VisibleForPackageGroup
    public static String getRelativePath(@NotNull File srcFile, @NotNull File dstFile) {
        return getRelativePath(Util.getSystemAbsPath(srcFile), Util.getSystemAbsPath(dstFile));
    }

    @VisibleForPackageGroup
    public static void assertDocCount(Directory luceneDir, int expectedCount) throws Exception {
        IndexReader reader = IndexReader.open(luceneDir);
        assertEquals(expectedCount, reader.numDocs());
        Closeables.closeQuietly(reader);
    }

    @VisibleForPackageGroup
    public static void assertResultCount(Directory luceneDir, String query, int expectedCount) throws Exception {
        IndexSearcher searcher = new IndexSearcher(luceneDir, true);
        QueryParser parser = new QueryParser(IndexRegistry.LUCENE_VERSION, Fields.CONTENT.key(),
                IndexRegistry.getAnalyzer());
        Query queryObject = parser.parse(query);
        TopDocs topDocs = searcher.search(queryObject, Integer.MAX_VALUE);
        assertEquals(expectedCount, topDocs.totalHits);
        Closeables.closeQuietly(searcher);
    }

    /**
     * Returns true if the given TrueZIP file is a zip archive, false otherwise.
     * 
     * @see IndexingConfig#isZipArchive(String)
     */
    @VisibleForPackageGroup
    public static boolean isZipArchive(@NotNull TFile file) {
        /*
         * TODO post-release-1.1: Replace this hack if a future TrueZIP version ever
         * provides a way to distinguish between directories and zip archives.
         */
        if (file.isFile())
            return false;
        if (!file.isEntry())
            return new java.io.File(file.getPath()).isFile();
        return new TFile(Util.getParentFile(file), file.getName(), TArchiveDetector.NULL).isFile();
    }

    /**
     * Tries to unpack the given zip archive entry. Returns null if the given
     * file is not a zip archive entry.
     */
    @Nullable
    @VisibleForPackageGroup
    public static File maybeUnpackZipEntry(@NotNull IndexingConfig config, @NotNull File file)
            throws DiskSpaceException, IndexingException {
        if (!(file instanceof TFile))
            return null;
        TFile tzFile = (TFile) file;
        if (!tzFile.isEntry())
            return null;
        long requiredSpace = tzFile.length();
        config.checkDiskSpaceInTempDir(requiredSpace);
        File unpackedFile = config.createDerivedTempFile(file.getName());
        try {
            tzFile.cp(unpackedFile);
            return unpackedFile;
        } catch (IOException e) {
            throw new IndexingException(e);
        }
    }

    public static boolean isUnmodifiedArchive(@NotNull Folder<?, ?> folder, @Nullable Long newLastModified) {
        Long oldLastModified = folder.getLastModified();
        if (oldLastModified != null && newLastModified != null && oldLastModified.equals(newLastModified))
            return true;
        return false;
    }

    // Reports the given error and saves it in the given tree node
    public static final void fail(@NotNull IndexingReporter reporter, @NotNull ErrorType type,
            @NotNull TreeNode treeNode, @Nullable Throwable cause) {
        IndexingError error = new IndexingError(type, treeNode, cause);
        treeNode.setError(error);
        reporter.fail(error);
    }

}