package com.ichi2.anki.service; import java.io.File; import java.io.FileFilter; import java.sql.SQLException; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import java.util.concurrent.locks.ReentrantLock; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.ichi2.anki.BackupManager; import com.ichi2.anki.model.Deck; public class DeckManager { public static Logger log = LoggerFactory.getLogger(DeckManager.class); public static final int REQUESTING_ACTIVITY_STUDYOPTIONS = 0; public static final int REQUESTING_ACTIVITY_DECKPICKER = 1; public static final int REQUESTING_ACTIVITY_WIDGETSTATUS = 2; public static final int REQUESTING_ACTIVITY_BIGWIDGET = 3; public static final int REQUESTING_ACTIVITY_STATISTICS = 4; public static final int REQUESTING_ACTIVITY_SYNCCLIENT = 5; public static final int REQUESTING_ACTIVITY_CARDEDITOR = 6; public static final int REQUESTING_ACTIVITY_DOWNLOADMANAGER = 7; private static HashMap<String, DeckInformation> sLoadedDecks = new HashMap<String, DeckInformation>(); private static HashMap<String, ReentrantLock> sDeckLocks = new HashMap<String, ReentrantLock>(); private String deckPath = "E:\\Project\\Java Anki\\Decks"; private static String mainDeckPath; public DeckManager() { } public synchronized static Deck getDeck(String deckpath, boolean setAsMainDeck, boolean doSafetyBackupIfNeeded, int requestingActivity, boolean rebuild) { Deck deck = null; lockDeck(deckpath); try { if (sLoadedDecks.containsKey(deckpath)) { // do not open deck if already loaded DeckInformation deckInformation = sLoadedDecks.get(deckpath); deck = deckInformation.mDeck; } else { try { log.info("DeckManager: try to load deck " + deckpath + " (" + requestingActivity + ")"); /*if (doSafetyBackupIfNeeded) { BackupManager.safetyBackupNeeded(deckpath, BackupManager.SAFETY_BACKUP_THRESHOLD); } */ deck = Deck.openDeck(deckpath, rebuild, true); log.info("DeckManager: Deck loaded!"); sLoadedDecks.put(deckpath, new DeckInformation(deckpath, deck, requestingActivity, rebuild)); } catch (RuntimeException e) { log.error("DeckManager: deck " + deckpath + " could not be opened = " + e.getMessage(), e); BackupManager.restoreDeckIfMissing(deckpath); deck = null; } catch (SQLException e) { log.error("DeckManager: deck " + deckpath + " could not be opened = " + e.getMessage(), e); BackupManager.restoreDeckIfMissing(deckpath); deck = null; } } } finally { if (setAsMainDeck && deck != null) { mainDeckPath = deckpath; } unlockDeck(deckpath); } return deck; } /** * Return anki deck lists * * @return */ public Map<String, String> listAnkiDeck() { File decksPathFile = new File(deckPath); File[] fileList = decksPathFile.listFiles(new AnkiFilter()); Map<String, String> sDeckPaths = new HashMap<String, String>(); if (fileList != null && fileList.length > 0) { for (File file : fileList) { String name = file.getName().replaceAll(".anki", ""); sDeckPaths.put(name, file.getAbsolutePath()); } } return sDeckPaths; } public static void lockDeck(String path) { if (!sDeckLocks.containsKey(path)) { sDeckLocks.put(path, new ReentrantLock(true)); } sDeckLocks.get(path).lock(); } public static void unlockDeck(String path) { if (sDeckLocks.containsKey(path)) { sDeckLocks.get(path).unlock(); } } public static final class AnkiFilter implements FileFilter { public boolean accept(File pathname) { if (pathname.isFile() && pathname.getName().endsWith(".anki")) { return true; } return false; } } public static class DeckInformation { public String mKey; public Deck mDeck; public boolean mInitiallyRebuilt = true; //public boolean mDeleteJournalModeForced = false; public boolean mWaitForDeckTaskToFinish = false; public ArrayList<Integer> mOpenedBy = new ArrayList<Integer>(); DeckInformation(String key, Deck deck, int openedBy, boolean initiallyRebuilt) { this.mKey = key; this.mDeck = deck; this.mOpenedBy.add(openedBy); this.mInitiallyRebuilt = initiallyRebuilt; } } public static class CloseDeckInformation { public String mDeckPath; public int mCaller; CloseDeckInformation(String deckpath, int caller) { this.mDeckPath = deckpath; this.mCaller = caller; } } }