/* * Copyright 2003-2013 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package jetbrains.mps.extapi.persistence; import jetbrains.mps.vfs.FileSystemEvent; import org.jetbrains.mps.openapi.util.ProgressMonitor; import jetbrains.mps.vfs.FileSystem; import jetbrains.mps.vfs.FileSystemListener; import jetbrains.mps.vfs.IFile; import org.jetbrains.annotations.NotNull; import org.jetbrains.mps.openapi.persistence.ModelRoot; import java.util.Arrays; import java.util.Collections; /** * This data source allows to track a backup file along with the main file. * The name of the backup file is formed by adding a tilde (~) to the name of the original file. * The backup file stores the same data, but in a generic and reliable format. It becomes the main * storage when content doesn't fit into the original file because of the format restrictions. */ public class FileWithBackupDataSource extends FileDataSource { private BackupFileListener myBackupFileListener; public FileWithBackupDataSource(@NotNull IFile file, ModelRoot modelRoot) { super(file, modelRoot); } @NotNull public IFile getBackupFile() { IFile file = getFile(); return file.getFileSystem().getFile(file.getPath() + "~"); } @Override public long getTimestamp() { IFile backupFile = getBackupFile(); long backupTimestamp = backupFile.exists() ? backupFile.lastModified() : -1L; return Math.max(super.getTimestamp(), backupTimestamp); } @Override public void refresh() { super.refresh(); getBackupFile().refresh(); } @Override public void update(ProgressMonitor monitor, @NotNull FileSystemEvent event) { IFile mainFile = getFile(); IFile backupFile = getBackupFile(); boolean isChanged = false; for (IFile file : event.getChanged()) { if (file.equals(mainFile) || file.equals(backupFile)) { isChanged = true; break; } } for (IFile file : event.getRemoved()) { // main file deletion is handled by model roots if (file.equals(backupFile)) { isChanged = true; break; } } if (isChanged) { fireChanged(monitor); } } @Override protected void startListening() { super.startListening(); IFile backupFile = getBackupFile(); myBackupFileListener = new BackupFileListener(backupFile); backupFile.getFileSystem().addListener(myBackupFileListener); } @Override protected void stopListening() { getBackupFile().getFileSystem().removeListener(myBackupFileListener); myBackupFileListener = null; super.stopListening(); } @Override public Iterable<FileSystemListener> getListenerDependencies() { FileSystemListener backupFileListener = myBackupFileListener; FileSystemListener parent = getParentListener(); if (backupFileListener != null && parent != null) { return Arrays.asList(parent, backupFileListener); } if (parent != null) { return Collections.singleton(parent); } return backupFileListener != null ? Collections.singleton(backupFileListener) : null; } private class BackupFileListener implements FileSystemListener { private IFile path; private BackupFileListener(@NotNull IFile path) { this.path = path; } @NotNull @Override public IFile getFileToListen() { return path; } @Override public Iterable<FileSystemListener> getListenerDependencies() { return null; } @Override public void update(ProgressMonitor monitor, @NotNull FileSystemEvent event) { event.notify(FileWithBackupDataSource.this); } } public static FileWithBackupDataSource create(FileDataSource source) { return new FileWithBackupDataSource(source.getFile(), source.myModelRoot); } }