/**
* OpenAtlasForAndroid Project
* The MIT License (MIT) Copyright (OpenAtlasForAndroid) 2015 Bunny Blue,achellies
* <p>
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
* and associated documentation files (the "Software"), to deal in the Software
* without restriction, including without limitation the rights to use, copy, modify,
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to the following conditions:
* <p>
* The above copyright notice and this permission notice shall be included in all copies
* or substantial portions of the Software.
* <p>
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
* PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
* FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* @author BunnyBlue
**/
package com.openatlas.framework.bundlestorage;
import android.text.TextUtils;
import com.openatlas.framework.Framework;
import com.openatlas.log.Logger;
import com.openatlas.log.LoggerFactory;
import com.openatlas.runtime.RuntimeVariables;
import com.openatlas.util.OpenAtlasUtils;
import com.openatlas.util.StringUtils;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.List;
import java.util.SortedMap;
import java.util.TreeMap;
/**
* This is Bundle Archive implementation.<br>
* Archive BundleArchive BundleArchiveRevision() ,decorator mode,looks like Context
* ,ContextWrap and ContextImpl
* **/
public class BundleArchive implements Archive {
Logger log = LoggerFactory.getInstance("BundleArchive");
public static final String REVISION_DIRECTORY = "version";
public static final String DEPRECATED_MARK = "deprecated";
private File bundleDir;
private final BundleArchiveRevision currentRevision;
private final SortedMap<Long, BundleArchiveRevision> revisions;
public BundleArchive(String location, File bundleDir) throws IOException {
this.revisions = new TreeMap<Long, BundleArchiveRevision>();
File[] listFiles = bundleDir.listFiles();
String currentProcessName = OpenAtlasUtils.getProcessNameByPID(android.os.Process.myPid());
if (listFiles != null) {
for (File file : listFiles) {
if (file.getName().startsWith(REVISION_DIRECTORY)) {
if (new File(file, DEPRECATED_MARK).exists()) {
try {
if (!TextUtils.isEmpty(currentProcessName) && currentProcessName.equals(RuntimeVariables.androidApplication.getPackageName())) {
for (File delete : file.listFiles()) {
delete.delete();
}
file.delete();
}
} catch (Exception e) {
}
} else {
long parseLong = Long.parseLong(StringUtils.substringAfter(file.getName(), "."));
if (parseLong > 0) {
this.revisions.put(Long.valueOf(parseLong), null);
}
}
}
}
}
if (this.revisions.isEmpty()) {
try {
if (!TextUtils.isEmpty(currentProcessName) && currentProcessName.equals(RuntimeVariables.androidApplication.getPackageName())) {
for (File file : listFiles) {
file.delete();
}
bundleDir.delete();
}
} catch (Exception e2) {
}
throw new IOException("No valid revisions in bundle archive directory: " + bundleDir);
}
this.bundleDir = bundleDir;
long longValue = this.revisions.lastKey().longValue();
BundleArchiveRevision bundleArchiveRevision = new BundleArchiveRevision(location, longValue, new File(bundleDir, "version." + String.valueOf(longValue)));
this.revisions.put(Long.valueOf(longValue), bundleArchiveRevision);
this.currentRevision = bundleArchiveRevision;
//remove old version
for (int i = 1; i < longValue; i++) {
File mBundleDir = new File(bundleDir, "version." + String.valueOf(i));
if (mBundleDir.isDirectory()) {
File[] listFilesSub = mBundleDir.listFiles();
for (File file : listFilesSub) {
file.delete();
}
mBundleDir.delete();
}
log.info("remove old bundle@" + mBundleDir.getAbsolutePath() + " last version : " + currentRevision);
}
//remove old version
}
public BundleArchive(String location, File bundleDir, InputStream inputStream)
throws IOException {
this.revisions = new TreeMap<Long, BundleArchiveRevision>();
this.bundleDir = bundleDir;
BundleArchiveRevision bundleArchiveRevision = new BundleArchiveRevision(
location, 1, new File(bundleDir, "version." + String.valueOf(1)),
inputStream);
this.revisions.put(Long.valueOf(1), bundleArchiveRevision);
this.currentRevision = bundleArchiveRevision;
}
public BundleArchive(String location, File bundleDir, File archiveFile) throws IOException {
this.revisions = new TreeMap<Long, BundleArchiveRevision>();
this.bundleDir = bundleDir;
BundleArchiveRevision bundleArchiveRevision = new BundleArchiveRevision(
location, 1, new File(bundleDir, "version." + String.valueOf(1)), archiveFile);
this.revisions.put(Long.valueOf(1), bundleArchiveRevision);
this.currentRevision = bundleArchiveRevision;
}
@Override
public BundleArchiveRevision newRevision(String location, File bundleDir,
InputStream inputStream) throws IOException {
long longValue = 1 + this.revisions.lastKey().longValue();
BundleArchiveRevision bundleArchiveRevision = new BundleArchiveRevision(
location, longValue, new File(bundleDir, "version."
+ String.valueOf(longValue)), inputStream);
this.revisions.put(Long.valueOf(longValue), bundleArchiveRevision);
return bundleArchiveRevision;
}
@Override
public BundleArchiveRevision newRevision(String packageName, File bundleDir, File archiveFile)
throws IOException {
long revision = 1 + this.revisions.lastKey().longValue();
BundleArchiveRevision bundleArchiveRevision = new BundleArchiveRevision(
packageName, revision, new File(bundleDir, "version."
+ String.valueOf(revision)), archiveFile);
this.revisions.put(Long.valueOf(revision), bundleArchiveRevision);
return bundleArchiveRevision;
}
@Override
public BundleArchiveRevision getCurrentRevision() {
return this.currentRevision;
}
@Override
public File getArchiveFile() {
return this.currentRevision.getRevisionFile();
}
public File getBundleDir() {
return this.bundleDir;
}
@Override
public boolean isDexOpted() {
return this.currentRevision.isDexOpted();
}
@Override
public void optDexFile() {
this.currentRevision.optDexFile();
}
@Override
public InputStream openAssetInputStream(String name) throws IOException {
return this.currentRevision.openAssetInputStream(name);
}
@Override
public InputStream openNonAssetInputStream(String name) throws IOException {
return this.currentRevision.openNonAssetInputStream(name);
}
@Override
public Class<?> findClass(String clazz, ClassLoader classLoader)
throws ClassNotFoundException {
return this.currentRevision.findClass(clazz, classLoader);
}
@Override
public File findLibrary(String name) {
return this.currentRevision.findSoLibrary(name);
}
@Override
public List<URL> getResources(String name) throws IOException {
return this.currentRevision.getResources(name);
}
@Override
public void purge() throws Exception {
if (this.revisions.size() > 1) {
long revisionNum = this.currentRevision.getRevisionNum();
for (Long longValue : this.revisions.keySet()) {
long longValue2 = longValue.longValue();
if (longValue2 != revisionNum) {
File file = new File(this.bundleDir, "version."
+ String.valueOf(longValue2));
if (file.exists()) {
Framework.deleteDirectory(file);
}
}
}
this.revisions.clear();
this.revisions.put(Long.valueOf(revisionNum), this.currentRevision);
}
}
public static boolean downgradeRevision(File file) throws IOException {
File[] listFiles = file.listFiles(new FilenameFilter() {
@Override
public boolean accept(File dir, String filename) {
return !(!filename.startsWith(BundleArchive.REVISION_DIRECTORY) ||
new File(dir, BundleArchive.DEPRECATED_MARK).exists());
}
});
if (listFiles == null || listFiles.length <= 0) {
return false;
}
new File(listFiles[listFiles.length - 1], DEPRECATED_MARK).createNewFile();
return true;
}
@Override
public void close() {
}
/**
* @version start from 1.0.0
* **/
public static class MisMatchException extends RuntimeException {
public MisMatchException(String message) {
super(message);
}
public MisMatchException(String message, Throwable th) {
super(message, th);
}
}
}