20 package net.sf.gridarta.model.index;
23 import java.io.FileInputStream;
24 import java.io.FileNotFoundException;
25 import java.io.FileOutputStream;
26 import java.io.IOException;
27 import java.io.InputStream;
28 import java.io.ObjectInputStream;
29 import java.io.ObjectOutputStream;
30 import java.io.OutputStream;
31 import java.nio.charset.StandardCharsets;
32 import java.util.Arrays;
33 import java.util.concurrent.Semaphore;
47 import org.apache.log4j.Category;
48 import org.apache.log4j.Logger;
49 import org.jetbrains.annotations.NotNull;
50 import org.jetbrains.annotations.Nullable;
162 public void mapCreated(@NotNull
final MapControl<G, A, R> mapControl,
final boolean interactive) {
163 if (!mapControl.isPickmap()) {
175 if (!mapControl.isPickmap()) {
190 final MapFile mapFile = mapControl.getMapModel().getMapFile();
191 if (mapFile != null) {
206 public void mapsDirectoryChanged(@NotNull
final File mapsDirectory) {
222 public void valueAdded(@NotNull
final MapFile value) {
227 public void valueRemoved(@NotNull
final MapFile value) {
232 public void nameChanged() {
237 public void pendingChanged() {
238 mapsIndexSemaphore.release();
242 public void indexingFinished() {
256 while (!Thread.currentThread().isInterrupted()) {
259 if (state == State.IDLE) {
261 mapsIndexSemaphore.acquire();
262 }
catch (
final InterruptedException ignored) {
263 Thread.currentThread().interrupt();
276 private final Thread
thread =
new Thread(runnable,
"indexer");
310 public void stop() throws InterruptedException {
318 if (!mapControl.isPickmap()) {
319 mapControl.removeMapControlListener(mapControlListener);
337 while (state != State.IDLE || mapsIndex.
hasPending()) {
338 syncState.wait(1000L);
348 final File tmpMapsDirectory;
350 if (newMapsDirectory == null || (mapsDirectory != null && mapsDirectory.equals(newMapsDirectory))) {
359 assert tmpMapsDirectory != null;
360 mapsDirectory = tmpMapsDirectory;
361 newMapsDirectory = null;
374 assert Thread.holdsLock(syncMapsDirectory);
375 if (mapsDirectory == null) {
387 assert mapsDirectory != null;
390 try (OutputStream outputStream =
new FileOutputStream(cacheFile)) {
391 try (ObjectOutputStream objectOutputStream =
new ObjectOutputStream(outputStream)) {
392 mapsIndex.
save(objectOutputStream);
395 if (LOG.isInfoEnabled()) {
396 LOG.info(cacheFile +
": saved " + mapsIndex.
size() +
" entries");
398 }
catch (
final IOException ex) {
399 LOG.warn(cacheFile +
": cannot save cache file: " + ex.getMessage());
407 assert Thread.holdsLock(syncMapsDirectory);
408 assert mapsDirectory != null;
411 try (InputStream inputStream =
new FileInputStream(cacheFile)) {
412 try (ObjectInputStream objectInputStream =
new ObjectInputStream(inputStream)) {
413 mapsIndex.
load(objectInputStream);
416 if (LOG.isInfoEnabled()) {
417 LOG.info(cacheFile +
": loaded " + mapsIndex.
size() +
" entries");
419 }
catch (
final FileNotFoundException ex) {
420 if (LOG.isDebugEnabled()) {
421 LOG.debug(cacheFile +
": cache file does not exist: " + ex.getMessage());
424 }
catch (
final IOException ex) {
425 LOG.warn(cacheFile +
": cannot load cache file: " + ex.getMessage());
436 final File[] files = dir.getFile().listFiles();
441 for (
final File file : files) {
443 if (file.isFile() && !file.getName().endsWith(
"~")) {
444 mapsIndex.
add(mapFile, file.lastModified());
445 }
else if (file.isDirectory() && !file.getName().equalsIgnoreCase(
".svn") && !file.getName().equalsIgnoreCase(
".dedit")) {
457 if (mapFile == null) {
463 final long timestamp = mapFile.
getFile().lastModified();
466 mapControl = mapManager.
openMapFile(mapFile,
false);
467 }
catch (
final IOException ex) {
468 if (LOG.isInfoEnabled()) {
469 LOG.info(mapFile +
": load failed:" + ex.getMessage());
475 if (LOG.isDebugEnabled()) {
476 LOG.debug(mapFile +
": indexing as '" + mapName +
"'");
478 mapsIndex.
setName(mapFile, timestamp, mapName);
480 mapManager.
release(mapControl);
491 final byte[] key =
new byte[16];
494 data = mapsDirectory.getAbsoluteFile().toString().getBytes(StandardCharsets.UTF_8);
495 final byte[] hash =
new byte[8];
496 final byte[] in =
new byte[8];
497 final byte[] out =
new byte[8];
499 for (i = 0; i + 8 < data.length; i++) {
500 System.arraycopy(data, i, in, 0, 8);
502 for (
int j = 0; j < 8; j++) {
506 final int len = data.length % 8;
507 System.arraycopy(data, i, in, 0, len);
509 Arrays.fill(in, len + 1, 8, (byte) 0);
511 for (
int j = 0; j < 8; j++) {
515 final StringBuilder sb =
new StringBuilder(
"index/maps/");
516 for (
int j = 0; j < 8; j++) {
517 sb.append(String.format(
"%02x", hash[j] & 0xff));
519 sb.append(mapsDirectory.getName());
521 final File dir = file.getParentFile();
522 if (dir != null && !dir.exists() && !dir.mkdirs()) {
523 LOG.warn(
"cannot create directory: " + dir);
532 private void setState(@NotNull
final State state) {
534 if (this.state == state) {
540 syncState.notifyAll();
548 if (LOG.isDebugEnabled()) {
549 LOG.debug(
"state=" + state);
552 if (state == State.IDLE) {
Indexes maps by map name.
void removeMapManagerListener(@NotNull MapManagerListener< G, A, R > listener)
Removes a MapManagerListener to be notified.
void encrypt(@NotNull final byte[] plaintext, @NotNull final byte[] ciphertext)
Encrypts a data block.
void removeIndexListener(@NotNull final IndexListener< V > listener)
Removes an IndexListener to be notified of changes.
final ProjectSettingsListener projectSettingsListener
The ProjectSettingsListener attached to projectSettings for tracking changed maps directories...
A MapManager manages all opened maps.
void release(@NotNull MapControl< G, A, R > mapControl)
Releases a MapControl instance.
final Object syncMapsDirectory
The object for synchronizing access to mapsDirectory and newMapsDirectory.
void setPending(@NotNull final V value)
Marks a value as pending.
boolean hasPending()
Returns whether at least one pending value exists.
final Semaphore mapsIndexSemaphore
A pending map may exist in mapsIndex if a permit is available.
Settings that apply to a project.
void start()
Starts indexing.
Maintains a MapsIndex for the maps directory.
Interface for listeners interested in Index related events.
void updateMapsDirectory()
Checks whether newMapsDirectory has been set and updates mapsDirectory accordingly.
static File getCacheFile(@NotNull final File mapsDirectory)
Returns the cache file for a given maps directory.
Implements the XTEA algorithm.
File newMapsDirectory
The maps directory to index.
static File getHomeFile(@NotNull final String filename)
Return the filename to use when dealing with this application's and current users' home directory...
INIT
Indexer has been created but is not yet running.
void load(@NotNull final ObjectInputStream objectInputStream)
unchecked
INDEX
Indexer is indexing maps differing from the current index.
V removePending()
Returns one pending value.
final Runnable runnable
The Runnable scanning the maps directory and updating the index.
final MapControlListener< G, A, R > mapControlListener
The MapControlListener attached to all opened maps.
void setName(@NotNull final V value, final long timestamp, @NotNull final String name)
Associates a value with a name.
MapModel< G, A, R > getMapModel()
Returns the map model.
final Thread thread
The Thread executing runnable.
void endUpdate()
Ends an update.
void addIndexListener(@NotNull final IndexListener< V > listener)
Adds an IndexListener to be notified of changes.
Base package of all Gridarta classes.
Reflects a game object (object on a map).
static final Category LOG
The Logger for printing log messages.
Loader for loading resources the user's home directory.
Interface for listeners listening to MapManager changes.
final MapsIndex mapsIndex
The MapsIndex being updated.
Interface for listeners listening on changes in MapControl instances.
void indexingFinished()
Should be called after indexing has finished.
GameObjects are the objects based on Archetypes found on maps.
int size()
Returns the number of values in this cache.
void waitForIdle()
Blocks the calling thread until all pending maps have been indexed.
void stop()
Stops indexing.
void save(@NotNull final ObjectOutputStream objectOutputStream)
Saves the state to an ObjectOutputStream.
MapsIndexer(@NotNull final MapsIndex mapsIndex, @NotNull final MapManager< G, A, R > mapManager, @NotNull final ProjectSettings projectSettings)
Creates a new instance.
File mapsDirectory
The currently indexed maps directory.
void addProjectSettingsListener(@NotNull ProjectSettingsListener listener)
Adds a ProjectSettingsListener to be notified of changes.
final ProjectSettings projectSettings
The ProjectSettings instance.
void scanMapsDirectoryInt(@NotNull final MapFile dir, @NotNull final String mapPath)
Scans a directory for files.
void removeProjectSettingsListener(@NotNull ProjectSettingsListener listener)
Removes a ProjectSettingsListener to be notified of changes.
boolean saveIndices()
Returns whether indices should be saved to disk.
A getMapArchObject()
Returns the Map Arch Object with the meta information about the map.
void addMapManagerListener(@NotNull MapManagerListener< G, A, R > listener)
Adds a MapManagerListener to be notified.
Currently nothing more than a marker interface for unification.
File getFile()
Returns a File for this map file.
void saveMapsIndex()
Saves mapsIndex to its cache file if modified.
void add(@NotNull final V value, final long timestamp)
Adds a value to the cache.
File getMapsDirectory()
Returns the default maps directory.
Interface for listeners interested in ProjectSettings events.
void setState(@NotNull final State state)
Sets a new value to state.
final IndexListener< MapFile > indexListener
The IndexListener attached to mapsIndex to detect newly added pending maps.
final MapManager< G, A, R > mapManager
The MapManager for loading map files.
void clear()
Clears all values from the index.
boolean isModified()
Returns whether the state was modified since last save.
void loadMapsIndex()
Loads mapsIndex from its cache file.
MapControl< G, A, R > openMapFile(@NotNull MapFile mapFile, boolean interactive)
Loads a map file.
void reportStateChange()
Logs a changed value of state.
final Object syncState
The object for synchronizing access to state.
void indexPendingMaps()
Indexes one pending map from mapsIndex.
State state
The indexer state.
SCAN
Indexer is scanning the maps directory searching for files differing to the current index...
final MapManagerListener< G, A, R > mapManagerListener
The MapManagerListener attached to mapManager to detect current map changes.
List< MapControl< G, A, R > > getOpenedMaps()
Returns all opened maps.
The location of a map file with a map directory.
Interface for MapArchObjects.
void beginUpdate()
Starts an update.