/* Copyright 2004-2014 Jim Voris
*
* 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 com.qumasoft.qvcslib;
import com.qumasoft.qvcslib.commandargs.CreateArchiveCommandArgs;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.event.ChangeListener;
/**
* Directory Manager. Manage the combined (merged) model of workfiles and archive files.
* @author Jim Voris
*/
public class DirectoryManager implements DirectoryManagerInterface {
private ArchiveDirManagerInterface archiveDirManager;
private WorkfileDirectoryManagerInterface workfileDirectoryManager;
private final String userName;
private final String projectName;
private final String viewName;
private boolean hasChangedFlag = false;
private AbstractProjectProperties projectProperties;
private final Map<String, MergedInfoInterface> mergedMap = Collections.synchronizedMap(new TreeMap<String, MergedInfoInterface>());
// Create our logger object
private static final Logger LOGGER = Logger.getLogger("com.qumasoft.qvcslib");
/**
* Creates a new instance of DirectoryManager.
* @param user user name.
* @param project project name.
* @param view view name.
*/
public DirectoryManager(String user, String project, String view) {
this.userName = user;
this.projectName = project;
this.viewName = view;
}
@Override
public ArchiveDirManagerInterface getArchiveDirManager() {
return archiveDirManager;
}
void setArchiveDirManager(ArchiveDirManagerInterface archiveDirMgr) {
this.archiveDirManager = archiveDirMgr;
this.projectProperties = archiveDirMgr.getProjectProperties();
}
@Override
public WorkfileDirectoryManagerInterface getWorkfileDirectoryManager() {
return workfileDirectoryManager;
}
/**
* Set the workfile directory manager.
* @param workfileDirManager the workfile directory manager.
*/
public void setWorkfileDirectoryManager(WorkfileDirectoryManagerInterface workfileDirManager) {
this.workfileDirectoryManager = workfileDirManager;
}
@Override
public boolean createArchive(CreateArchiveCommandArgs commandLineArgs, String filename) throws IOException, QVCSException {
if (archiveDirManager != null) {
return archiveDirManager.createArchive(commandLineArgs, filename, null);
} else {
throw new QVCSRuntimeException("createArchive called but archiveDirManager is null!!");
}
}
/**
* Merge the entries from the archive directory and the entries from the workfile directory into a single combined view of the set of the files.
* @throws QVCSException the archive directory manager or the workfile directory manager is null.
*/
@Override
@SuppressWarnings("FinallyDiscardsException")
public synchronized void mergeManagers() throws QVCSException {
// The archive directory manager and workfile directory managers must
// be defined...
if ((archiveDirManager == null)
|| (workfileDirectoryManager == null)) {
LOGGER.log(Level.SEVERE, "archive directory manager or workfile directory manager is not defined!!");
throw new QVCSException("archive directory manager or workfile directory manager is not defined!!");
}
LOGGER.log(Level.FINE, "DirectoryManager.mergeManagers for " + getProjectName() + "//" + getAppendedPath() + " on thread: " + Thread.currentThread().getName());
// Do this in a while loop so we'll repeat the merge if we catch a
// concurrent modification exception. This latter can happen if we
// get an update from the server while the merge is in progress.
while (true) {
boolean concurrentExceptionThrown = false;
try {
// Make sure to start fresh.
mergedMap.clear();
// Add the workfiles first.
Iterator<WorkfileInfoInterface> workfilesIterator = getWorkfileDirectoryManager().getWorkfileCollection().iterator();
while (workfilesIterator.hasNext()) {
WorkfileInfoInterface workfileInfo = workfilesIterator.next();
MergedInfoInterface mergedInfo = new MergedInfo(workfileInfo, getArchiveDirManager(), getArchiveDirManager().getProjectProperties(), getUserName());
mergedMap.put(mergedInfo.getMergedInfoKey(), mergedInfo);
}
// Add the archives, merging them with the existing workfile entries
// and/or creating new entries for archives that do not have
// corresponding workfiles.
Iterator<ArchiveInfoInterface> archivesIterator = getArchiveDirManager().getArchiveInfoCollection().values().iterator();
while (archivesIterator.hasNext()) {
ArchiveInfoInterface archiveInfo = archivesIterator.next();
MergedInfoInterface mergedInfo = getMergedInfo(archiveInfo.getShortWorkfileName());
if (mergedInfo == null) {
mergedInfo = new MergedInfo(archiveInfo, getArchiveDirManager(), getArchiveDirManager().getProjectProperties(), getUserName());
mergedMap.put(mergedInfo.getMergedInfoKey(), mergedInfo);
} else {
mergedInfo.setArchiveInfo(archiveInfo);
mergedInfo.getWorkfileInfo().setKeywordExpansionAttribute(archiveInfo.getAttributes().getIsExpandKeywords());
}
}
setHasChanged(true);
} catch (java.util.ConcurrentModificationException e) {
LOGGER.log(Level.INFO, e.getClass().toString() + ":" + e.getLocalizedMessage());
concurrentExceptionThrown = true;
} catch (Exception e) {
throw new QVCSException(e.getClass().toString() + ":" + e.getLocalizedMessage());
} finally {
if (concurrentExceptionThrown) {
LOGGER.log(Level.INFO, "Will re-try building of merged information for [" + getAppendedPath() + "]");
} else {
break;
}
}
}
}
@Override
public Collection<MergedInfoInterface> getMergedInfoCollection() {
Collection<MergedInfoInterface> collection;
synchronized (mergedMap) {
collection = new ArrayList<>(mergedMap.values());
}
return collection;
}
@Override
public MergedInfoInterface getMergedInfo(String shortWorkfileName) {
return mergedMap.get(getMergedMapKey(shortWorkfileName));
}
@Override
public void addChangeListener(ChangeListener listener) {
archiveDirManager.addChangeListener(listener);
}
@Override
public String getAppendedPath() {
return archiveDirManager.getAppendedPath();
}
@Override
public AbstractProjectProperties getProjectProperties() {
return archiveDirManager.getProjectProperties();
}
@Override
public void removeChangeListener(ChangeListener listener) {
archiveDirManager.removeChangeListener(listener);
}
@Override
public int getCount() {
return mergedMap.size();
}
@Override
public String getUserName() {
return userName;
}
@Override
public String getProjectName() {
return projectName;
}
@Override
public String getViewName() {
return viewName;
}
@Override
public boolean getHasChanged() {
return hasChangedFlag;
}
@Override
public void setHasChanged(boolean flag) {
hasChangedFlag = flag;
}
private String getMergedMapKey(String shortWorkfileName) {
String key;
if (projectProperties.getIgnoreCaseFlag()) {
key = shortWorkfileName.toLowerCase();
} else {
key = shortWorkfileName;
}
return key;
}
}