	private void build() {
		final BooksDatabase db = BooksDatabase.Instance();
		final FileInfoSet fileInfos = new FileInfoSet();
		final Map<Long,Book> savedBooksByFileId = db.loadBooks(fileInfos, true);
		final Map<Long,Book> savedBooksByBookId = new HashMap<Long,Book>();
		for (Book b : savedBooksByFileId.values()) {
			savedBooksByBookId.put(b.getId(), b);
		}
		for (long id : db.loadRecentBookIds()) {
			Book book = savedBooksByBookId.get(id);
			if (book == null) {
				book = Book.getById(id);
			}
			if (book != null) {
				new BookTree(getFirstLevelTree(ROOT_RECENT), book, true);
			}
		}
		for (long id : db.loadFavoritesIds()) {
			Book book = savedBooksByBookId.get(id);
			if (book == null) {
				book = Book.getById(id);
			}
			if (book != null) {
				getFirstLevelTree(ROOT_FAVORITES).getBookSubTree(book, true);
			}
		}
		fireModelChangedEvent();
		if (savedBooksByFileId.size() > 10) {
			final HashSet<String> letterSet = new HashSet<String>();
			for (Book book : savedBooksByFileId.values()) {
				final String letter = TitleTree.firstTitleLetter(book);
				if (letter != null) {
					letterSet.add(letter);
				}
			}
			myDoGroupTitlesByFirstLetter = savedBooksByFileId.values().size() > letterSet.size() * 5 / 4;
		}
		final Set<Book> orphanedBooks = new HashSet<Book>();
		for (Book book : savedBooksByFileId.values()) {
			synchronized (this) {
				if (book.File.exists()) {
					boolean doAdd = true;
					final ZLPhysicalFile file = book.File.getPhysicalFile();
					if (file == null) {
						continue;
					}
					if (!fileInfos.check(file, true)) {
						if (book.readMetaInfo()) {
							book.save();
						} else {
							doAdd = false;
						}
						file.setCached(false);
					}
					if (doAdd) {
						addBookToLibrary(book);
					}
				} else {
					myRootTree.removeBook(book);
					orphanedBooks.add(book);
				}
			}
			fireModelChangedEvent();
		}
		db.setExistingFlag(orphanedBooks, false);
		final Map<Long,Book> orphanedBooksByFileId = db.loadBooks(fileInfos, false);
		final Set<Book> newBooks = new HashSet<Book>();
		final List<ZLPhysicalFile> physicalFilesList = collectPhysicalFiles();
		for (ZLPhysicalFile file : physicalFilesList) {
			collectBooks(
				file, fileInfos,
				savedBooksByFileId, orphanedBooksByFileId,
				newBooks,
				!fileInfos.check(file, true)
			);
			file.setCached(false);
		}
		final ZLFile helpFile = getHelpFile();
		Book helpBook = savedBooksByFileId.get(fileInfos.getId(helpFile));
		if (helpBook == null) {
			helpBook = new Book(helpFile);
			helpBook.readMetaInfo();
		}
		addBookToLibrary(helpBook);
		fireModelChangedEvent();
		fileInfos.save();
		db.executeAsATransaction(new Runnable() {
			public void run() {
				for (Book book : newBooks) {
					book.save();
				}
			}
		});
		db.setExistingFlag(newBooks, true);
	}

	private void build() {
		final BooksDatabase db = BooksDatabase.Instance();
		final FileInfoSet fileInfos = new FileInfoSet();
		final Map<Long,Book> savedBooksByFileId = db.loadBooks(fileInfos, true);
		final Map<Long,Book> savedBooksByBookId = new HashMap<Long,Book>();
		for (Book b : savedBooksByFileId.values()) {
			savedBooksByBookId.put(b.getId(), b);
		}
		for (long id : db.loadRecentBookIds()) {
			Book book = savedBooksByBookId.get(id);
			if (book == null) {
				book = Book.getById(id);
				if (book != null && !book.File.exists()) {
					book = null;
				}
			}
			if (book != null) {
				new BookTree(getFirstLevelTree(ROOT_RECENT), book, true);
			}
		}
		for (long id : db.loadFavoritesIds()) {
			Book book = savedBooksByBookId.get(id);
			if (book == null) {
				book = Book.getById(id);
				if (book != null && !book.File.exists()) {
					book = null;
				}
			}
			if (book != null) {
				getFirstLevelTree(ROOT_FAVORITES).getBookSubTree(book, true);
			}
		}
		fireModelChangedEvent();
		if (savedBooksByFileId.size() > 10) {
			final HashSet<String> letterSet = new HashSet<String>();
			for (Book book : savedBooksByFileId.values()) {
				final String letter = TitleTree.firstTitleLetter(book);
				if (letter != null) {
					letterSet.add(letter);
				}
			}
			myDoGroupTitlesByFirstLetter = savedBooksByFileId.values().size() > letterSet.size() * 5 / 4;
		}
		final Set<Book> orphanedBooks = new HashSet<Book>();
		for (Book book : savedBooksByFileId.values()) {
			synchronized (this) {
				if (book.File.exists()) {
					boolean doAdd = true;
					final ZLPhysicalFile file = book.File.getPhysicalFile();
					if (file == null) {
						continue;
					}
					if (!fileInfos.check(file, true)) {
						if (book.readMetaInfo()) {
							book.save();
						} else {
							doAdd = false;
						}
						file.setCached(false);
					}
					if (doAdd) {
						addBookToLibrary(book);
					}
				} else {
					myRootTree.removeBook(book);
					orphanedBooks.add(book);
				}
			}
			fireModelChangedEvent();
		}
		db.setExistingFlag(orphanedBooks, false);
		final Map<Long,Book> orphanedBooksByFileId = db.loadBooks(fileInfos, false);
		final Set<Book> newBooks = new HashSet<Book>();
		final List<ZLPhysicalFile> physicalFilesList = collectPhysicalFiles();
		for (ZLPhysicalFile file : physicalFilesList) {
			collectBooks(
				file, fileInfos,
				savedBooksByFileId, orphanedBooksByFileId,
				newBooks,
				!fileInfos.check(file, true)
			);
			file.setCached(false);
		}
		final ZLFile helpFile = getHelpFile();
		Book helpBook = savedBooksByFileId.get(fileInfos.getId(helpFile));
		if (helpBook == null) {
			helpBook = new Book(helpFile);
			helpBook.readMetaInfo();
		}
		addBookToLibrary(helpBook);
		fireModelChangedEvent();
		fileInfos.save();
		db.executeAsATransaction(new Runnable() {
			public void run() {
				for (Book book : newBooks) {
					book.save();
				}
			}
		});
		db.setExistingFlag(newBooks, true);
	}
