SymbolConverterBase.java

/*
 * Copyright (c) 2021 by k3b.
 *
 * This file is part of of k3b-geoHelper library and LocationMapViewer.
 *
 * This program is free software: you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program 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 General Public License
 * for more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program. If not, see <http://www.gnu.org/licenses/>
 */

package de.k3b.geo;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import java.util.HashMap;
import java.util.Map;

import de.k3b.geo.api.GeoPointDto;
import de.k3b.geo.api.IGeoInfoHandler;
import de.k3b.geo.api.IGeoPointInfo;

/**
 * A {@link IGeoInfoHandler} in a chain of {@link IGeoInfoHandler}s that converts
 * {@link IGeoPointInfo#getSymbol()}s from "relative to the containing geo-file" to absolute.
 *
 * @param <T> OS-Specific File-Translations system
 */
public abstract class SymbolConverterBase<T>  implements IGeoInfoHandler {
    @NonNull protected final T rootDir;
    @NonNull protected final Map<String, T> name2file;
    @Nullable protected final IGeoInfoHandler nextConverter;

    /**
     * @param rootDir where all relative paths refer to
     * @param name2file a map that translates relative items to absolute items
     * @param nextConverter if not null: next converter in a chain to be executed
     */
    protected SymbolConverterBase(@NonNull final T rootDir, @Nullable final Map<String, T> name2file, @Nullable IGeoInfoHandler nextConverter) {
        this.rootDir = rootDir;
        this.name2file = name2file != null ? name2file : new HashMap<String, T>();
        this.nextConverter = nextConverter;
        if (this.name2file.size() == 0) {
            addFiles("", rootDir);
        }
    }

    // IGeoInfoHandler
    @Override
    public boolean onGeoInfo(IGeoPointInfo aGeoPoint) {
        String symbol = convertSymbol(aGeoPoint);
        if (symbol != null) {
            ((GeoPointDto) aGeoPoint).setSymbol(symbol);
        }
        if (nextConverter != null) nextConverter.onGeoInfo(aGeoPoint);
        return true;
    }

    private String convertSymbol(final IGeoPointInfo aGeoPoint) {
        String symbol = aGeoPoint != null ? aGeoPoint.getSymbol() : null;
        if (symbol != null && !symbol.contains(":") && symbol.contains(".")) {
            symbol = symbol.toLowerCase();
            T doc = name2file.get(symbol);
            if (doc == null && symbol.contains("/")) {
                doc = addFiles(symbol.split("/"));
            }
            if (doc != null) return getUri(doc);
        }
        return null;
    }

    private T addFiles(String[] pathElements) {
        T currentdir = rootDir;
        StringBuilder path = new StringBuilder();
        T doc = null;
        for (String pathElement : pathElements) {
            if (path.length() > 0) path.append("/");
            String parentPath = path.toString();
            path.append(pathElement);
            String pathLowerCase = path.toString();
            doc = name2file.get(pathLowerCase);
            if (doc == null) {
                addFiles(parentPath, currentdir);
                doc = name2file.get(pathLowerCase);
            }
            currentdir = doc;
        }
        return doc;
    }

    private void addFiles(String parentPath, T currentdir) {
        T[] children = listFiles(currentdir);
        if (children != null) {
            for (T child : children) {
                name2file.put(parentPath + getName(child).toLowerCase(), child);
            }
        }
    }

    public static boolean isGeo(String nameLower) {
        return GeoConfig.isOneOf(nameLower, GeoConfig.EXT_ALL);
    }

    public static boolean iszip(String nameLower) {
        return GeoConfig.isOneOf(nameLower, GeoConfig.EXT_ALL_ZIP);
    }

    // Abstract OS-Specific methods
    @NonNull protected abstract String getName(@NonNull T child);

    @Nullable protected abstract T[] listFiles(@Nullable T currentdir);

    @NonNull protected abstract String getUri(@NonNull T doc);

    protected abstract boolean isExistingDirectory(@Nullable T dir);
}