/*
* Syncany, www.syncany.org
* Copyright (C) 2011-2015 Philipp C. Heckel <philipp.heckel@gmail.com>
*
* 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 org.syncany.chunk;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;
/**
* The mime type chunker uses the file mime type delegate the chunking to
* either a <i>regular</i> chunker, or a <i>special</i> chunker.
*
* <p>The {@link #createChunks(File) createChunks()}-method uses the mime type of a file to
* determine which chunker is used. If the mime type matches a pattern in the list given in the constructor,
* the special chunker is used. If not, the regular chunker is used.
*
* <p>This is particularly useful to differentiate files that might change in a few bytes
* from files that hardly change at all (or change entirely) -- like image or video files.
*
* @author Philipp C. Heckel <philipp.heckel@gmail.com>
*/
public class MimeTypeChunker extends Chunker {
private static final Logger logger = Logger.getLogger(MimeTypeChunker.class.getSimpleName());
private Chunker regularChunker;
private Chunker specialChunker;
private List<Pattern> specialChunkerMimeTypes;
private Chunker delegatedChunker;
/**
* Creates a new mime type chunker.
*
* <p>The special chunker is used only if the mime type matches any of the mime types patterns
* in the list. Otherwise, the regular chunker is used.
*
* @param regularChunker The regular chunker is used if none of the mime types in the list matches
* @param specialChunker The special chunker is used if any of the mime type patterns matches
* @param specialChunkerMimeTypes List of mime type regex patterns used to determine which chunker to use
* @throws Exception If the two chunkers do not use the same checksum algorithm
*/
public MimeTypeChunker(Chunker regularChunker, Chunker specialChunker, List<String> specialChunkerMimeTypes) throws Exception {
if (!regularChunker.getChecksumAlgorithm().equals(specialChunker.getChecksumAlgorithm())) {
throw new Exception("Regular and special chunkers must use the same checksum algorithm.");
}
this.regularChunker = regularChunker;
this.specialChunker = specialChunker;
this.specialChunkerMimeTypes = initMimeTypePatterns(specialChunkerMimeTypes);
this.delegatedChunker = null;
}
@Override
public ChunkEnumeration createChunks(File file) throws IOException {
String mimeType = Files.probeContentType(Paths.get(file.getAbsolutePath()));
for (Pattern mimeTypePattern : specialChunkerMimeTypes) {
if (mimeType != null && mimeTypePattern.matcher(mimeType).matches()) {
logger.log(Level.INFO, "File mime type: " + mimeType + ", using SPECIAL chunker: " + file);
delegatedChunker = specialChunker;
return delegatedChunker.createChunks(file);
}
}
logger.log(Level.INFO, "File mime type: " + mimeType + ", using regular chunker: " + file);
delegatedChunker = regularChunker;
return delegatedChunker.createChunks(file);
}
@Override
public String toString() {
return "FileTypeBased";
}
@Override
public String getChecksumAlgorithm() {
return regularChunker.getChecksumAlgorithm();
}
private List<Pattern> initMimeTypePatterns(List<String> mimeTypePatternStrs) {
List<Pattern> patternList = new ArrayList<Pattern>();
for (String mimeTypePatternStr : mimeTypePatternStrs) {
patternList.add(Pattern.compile(mimeTypePatternStr));
}
return patternList;
}
}