/*
* Copyright (C) 2015-2017 PÂRIS Quentin
*
* 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 2 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, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.phoenicis.apps.repository;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.phoenicis.apps.dto.ApplicationDTO;
import org.phoenicis.apps.dto.CategoryDTO;
import org.phoenicis.apps.dto.ResourceDTO;
import org.phoenicis.apps.dto.ScriptDTO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.*;
import java.util.stream.Collectors;
public class LocalRepository implements Repository {
private final static Logger LOGGER = LoggerFactory.getLogger(LocalRepository.class);
private static final String CATEGORY_ICON_NAME = "icon.png";
private final String repositoryDirectory;
private final ObjectMapper objectMapper;
private final String repositorySource;
private LocalRepository(String repositoryDirectory, String repositorySource, ObjectMapper objectMapper) {
this.repositoryDirectory = repositoryDirectory;
this.objectMapper = objectMapper;
this.repositorySource = repositorySource;
}
private LocalRepository(String repositoryDirectory, ObjectMapper objectMapper) {
this(repositoryDirectory, repositoryDirectory, objectMapper);
}
@Override
public List<CategoryDTO> fetchInstallableApplications() {
final File repositoryDirectoryFile = new File(repositoryDirectory);
final File[] categoryDirectories = repositoryDirectoryFile.listFiles();
if (categoryDirectories == null) {
return Collections.emptyList();
}
LOGGER.info("Reading directory : " + repositoryDirectory);
return fetchCategories(categoryDirectories);
}
private List<CategoryDTO> fetchCategories(File[] categoryDirectories) {
final List<CategoryDTO> results = new ArrayList<>();
for (File categoryDirectory : categoryDirectories) {
if (categoryDirectory.isDirectory() && !categoryDirectory.getName().startsWith(".")) {
final File categoryFile = new File(categoryDirectory, "category.json");
final CategoryDTO.Builder categoryDTOBuilder = new CategoryDTO.Builder(
unSerializeCategory(categoryFile)).withName(categoryDirectory.getName())
.withApplications(fetchApplications(categoryDirectory));
final File categoryIconFile = new File(categoryDirectory, CATEGORY_ICON_NAME);
if (categoryIconFile.exists()) {
try {
categoryDTOBuilder.withIcon(new URI("file:///" + categoryIconFile.getAbsolutePath()));
} catch (URISyntaxException e) {
LOGGER.warn("Invalid icon path", e);
}
}
CategoryDTO category = categoryDTOBuilder.build();
results.add(category);
}
}
Collections.sort(results, Comparator.comparing(CategoryDTO::getName));
return results;
}
private List<ApplicationDTO> fetchApplications(File categoryDirectory) {
final File[] applicationDirectories = categoryDirectory.listFiles();
if (applicationDirectories == null) {
return Collections.emptyList();
}
final List<ApplicationDTO> results = new ArrayList<>();
for (File applicationDirectory : applicationDirectories) {
if (applicationDirectory.isDirectory()) {
final ApplicationDTO.Builder applicationDTOBuilder = new ApplicationDTO.Builder(
unSerializeApplication(new File(applicationDirectory, "application.json")));
if (StringUtils.isBlank(applicationDTOBuilder.getName())) {
applicationDTOBuilder.withName(applicationDirectory.getName());
}
final File miniaturesDirectory = new File(applicationDirectory, "miniatures");
if (miniaturesDirectory.exists() && miniaturesDirectory.isDirectory()) {
try {
applicationDTOBuilder.withMiniatures(fetchMiniatures(miniaturesDirectory));
} catch (IOException e) {
LOGGER.warn("Unable to read miniatures", e);
}
}
applicationDTOBuilder.withScripts(fetchScripts(applicationDirectory));
applicationDTOBuilder.withResources(fetchResources(applicationDirectory));
ApplicationDTO app = applicationDTOBuilder.build();
results.add(app);
}
}
Collections.sort(results, Comparator.comparing(ApplicationDTO::getName));
return results;
}
private List<URI> fetchMiniatures(File miniaturesDirectory) throws IOException {
final File[] miniatureFiles = miniaturesDirectory.listFiles();
return Arrays.stream(miniatureFiles)
.filter(miniatureFile -> !miniatureFile.isDirectory() && !miniatureFile.getName().startsWith("."))
.map(File::toURI).collect(Collectors.toList());
}
private List<ResourceDTO> fetchResources(File applicationDirectory) {
final File[] resources = new File(applicationDirectory, "resources").listFiles();
if (resources == null) {
return Collections.emptyList();
}
final List<ResourceDTO> results = new ArrayList<>();
for (File resourceFile : resources) {
if (!resourceFile.isDirectory() && !resourceFile.getName().startsWith(".")) {
try {
results.add(new ResourceDTO(resourceFile.getName(),
IOUtils.toByteArray(new FileInputStream(resourceFile))));
} catch (IOException ignored) {
}
}
}
return results;
}
private List<ScriptDTO> fetchScripts(File applicationDirectory) {
final File[] scriptDirectories = applicationDirectory.listFiles();
if (scriptDirectories == null) {
return Collections.emptyList();
}
final List<ScriptDTO> results = new ArrayList<>();
for (File scriptDirectory : scriptDirectories) {
if (scriptDirectory.isDirectory() && !"miniatures".equals(scriptDirectory.getName())
&& !"resources".equals(scriptDirectory.getName())) {
final ScriptDTO.Builder scriptDTOBuilder = new ScriptDTO.Builder(
unSerializeScript(new File(scriptDirectory, "script.json")));
scriptDTOBuilder.withScriptSource(repositorySource);
if (StringUtils.isBlank(scriptDTOBuilder.getScriptName())) {
scriptDTOBuilder.withScriptName(scriptDirectory.getName());
}
final File scriptFile = new File(scriptDirectory, "script.js");
if (scriptFile.exists()) {
try {
scriptDTOBuilder.withScript(new String(IOUtils.toByteArray(new FileInputStream(scriptFile))));
} catch (IOException e) {
LOGGER.warn("Script not found", e);
}
}
results.add(scriptDTOBuilder.build());
}
}
return results;
}
private CategoryDTO unSerializeCategory(File jsonFile) {
try {
return objectMapper.readValue(jsonFile, CategoryDTO.class);
} catch (IOException e) {
LOGGER.debug("JSON file not found", e);
return new CategoryDTO.Builder().build();
}
}
private ScriptDTO unSerializeScript(File jsonFile) {
try {
return objectMapper.readValue(jsonFile, ScriptDTO.class);
} catch (IOException e) {
LOGGER.debug("JSON file not found");
return new ScriptDTO.Builder().build();
}
}
private ApplicationDTO unSerializeApplication(File jsonFile) {
try {
return objectMapper.readValue(jsonFile, ApplicationDTO.class);
} catch (IOException e) {
LOGGER.debug("JSON file not found", e);
return new ApplicationDTO.Builder().build();
}
}
@Override
public String toString() {
return new ToStringBuilder(this).append("repositorySource", repositorySource)
.append("repositoryDirectory", repositoryDirectory).toString();
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
LocalRepository that = (LocalRepository) o;
EqualsBuilder builder = new EqualsBuilder();
builder.append(repositoryDirectory, that.repositoryDirectory);
return builder.isEquals();
}
@Override
public int hashCode() {
HashCodeBuilder builder = new HashCodeBuilder();
builder.append(repositoryDirectory);
return builder.toHashCode();
}
public static class Factory {
private final ObjectMapper objectMapper;
public Factory(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
public LocalRepository createInstance(String path) {
return new LocalRepository(path, objectMapper);
}
public LocalRepository createInstance(String path, String source) {
return new LocalRepository(path, source, objectMapper);
}
}
}