/*******************************************************************************
* Copyright (c) 2010 Arapiki Solutions Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* "Peter Smith <psmith@arapiki.com>" - initial API and
* implementation and/or initial documentation
*******************************************************************************/
package com.buildml.model.impl;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import com.buildml.model.BuildStoreVersionException;
import com.buildml.model.FatalBuildStoreError;
import com.buildml.model.IActionMgr;
import com.buildml.model.IActionTypeMgr;
import com.buildml.model.IBuildStore;
import com.buildml.model.IFileAttributeMgr;
import com.buildml.model.IFileGroupMgr;
import com.buildml.model.IFileIncludeMgr;
import com.buildml.model.IFileMgr;
import com.buildml.model.IPackageMemberMgr;
import com.buildml.model.IPackageMgr;
import com.buildml.model.IPackageRootMgr;
import com.buildml.model.IReportMgr;
import com.buildml.model.ISubPackageMgr;
import com.buildml.utils.errors.ErrorCode;
/**
* A BuildStore object is the main class that implements a BuildML build system. By creating
* a new BuildStore object, we create all the necessary data structures and databases
* to store an entire BuildML build.
*
* Note that although BuildStore is the main entry point, most of the work is done by
* its delegate classes, such as FileMgr, ActionMgr etc. These "Managers" each deal
* with a specific part of the build system, providing business logic and database access
* to implement features.
*
* @author "Peter Smith <psmith@arapiki.com>"
*/
public class BuildStore implements IBuildStore {
/*=====================================================================================*
* FIELDS/TYPES
*=====================================================================================*/
/**
* The BuildStoreDB manager object that manages our connection to the underlying
* database.
*/
private BuildStoreDB db;
/** The FileMgr manager object we'll delegate work to. */
private IFileMgr fileMgr;
/** The FileGroupMgr manager object we'll delegate work to. */
private IFileGroupMgr fileGroupMgr;
/** The FileIncludeMgr object we'll delegate work to. */
private IFileIncludeMgr fileIncludeMgr;
/** The ActionMgr manager object we'll delegate work to. */
private IActionMgr actionMgr;
/** The ActionTypeMgr manager object we'll delegate work to. */
private IActionTypeMgr actionTypeMgr;
/** The Reports manager object we'll delegate work to. */
private IReportMgr reportMgr;
/** The FileAttributeMgr object we'll delegate work to. */
private IFileAttributeMgr fileAttrMgr;
/** The Packages manager object we'll delegate work to. */
private IPackageMgr packages;
/** The IPackageMemberMgr object we'll delegate work to. */
private IPackageMemberMgr pkgMemberMgr;
/** The PackageRootMgr object we'll delegate work to */
private IPackageRootMgr pkgRootMgr;
/** The SubPackageMgr object we'll delegate work to */
private ISubPackageMgr subPkgMgr;
/** The SlotMgr associated with this BuildStore */
private SlotMgr slotMgr;
/*=====================================================================================*
* CONSTRUCTORS
*=====================================================================================*/
/**
* Open or create a new BuildStore database. Clients should not invoke this
* constructor directly. Instead, use BuildStoreFactory.
*
* @param buildStoreName Name of the database to open or create.
* @param saveRequired True if BuildStore must explicitly be "saved" before
* it's closed (otherwise the changes will be discarded).
* @param createIfNeeded True if the database should be initialized if it's not already.
* @throws FileNotFoundException The database file can't be found, or isn't writable.
* @throws IOException An I/O problem occurred while opening the database file.
* @throws BuildStoreVersionException The database schema of an existing database is the wrong version.
*/
public BuildStore(String buildStoreName, boolean saveRequired, boolean createIfNeeded)
throws FileNotFoundException, IOException, BuildStoreVersionException {
boolean freshDatabase = false;
/* create a new DB manager to handle all the SQL connection issues */
db = new BuildStoreDB(buildStoreName, saveRequired);
/* if necessary, initialize the database with required tables */
int buildStoreVersion = getBuildStoreVersion();
if (buildStoreVersion == -1){
if (createIfNeeded) {
/* changes must be committed promptly */
db.setFastAccessMode(false);
db.initDatabase();
freshDatabase = true;
} else {
throw new BuildStoreVersionException("File \"" + buildStoreName + "\" does not " +
"exist, or isn't a valid BuildML database file.");
}
}
/*
* Else, it's an existing database. Validate that the schema is the
* correct version.
*/
else {
if (buildStoreVersion < BuildStoreDB.SCHEMA_VERSION){
throw new BuildStoreVersionException(
"Database \"" + buildStoreName + "\" has an older schema version ("
+ buildStoreVersion + "). Please run \"bmladmin upgrade\" to upgrade " +
"the database content.");
}
if (buildStoreVersion > BuildStoreDB.SCHEMA_VERSION){
throw new BuildStoreVersionException(
"Database \"" + buildStoreName + "\" has an unrecognized schema version (" +
buildStoreVersion + "). It can only be read by a newer version of BuildML.");
}
}
/*
* Create a bunch of manager objects that we'll delegate work to.
*/
fileMgr = new FileMgr(this);
fileGroupMgr = new FileGroupMgr(this);
fileIncludeMgr = new FileIncludeMgr(this);
slotMgr = new SlotMgr(this);
actionMgr = new ActionMgr(this);
actionTypeMgr = new ActionTypeMgr(this);
reportMgr = new ReportMgr(this);
fileAttrMgr = new FileAttributeMgr(this, fileMgr);
packages = new PackageMgr(this);
subPkgMgr = new SubPackageMgr(this);
pkgMemberMgr = new PackageMemberMgr(this);
pkgRootMgr = new PackageRootMgr(this);
/*
* Since these managers were initialized in a specific order, some of the earlier
* managers might need extra initialization that depends on the later managers.
*/
((FileMgr)fileMgr).initPass2();
((ActionMgr)actionMgr).initPass2();
((SubPackageMgr)subPkgMgr).initPass2();
((PackageMgr)packages).initPass2();
/*
* When the database is first created, it won't have the "workspace" root set.
* By default, we set it to the parent directory of the build.bml file.
*/
if (freshDatabase) {
File dbFile = new File(db.getDatabaseFileName());
File workspaceDir = dbFile.getParentFile();
if (workspaceDir == null) {
throw new FatalBuildStoreError("Unable to determine initial \"workspace\" directory.");
}
int workspacePathId = fileMgr.addDirectory(workspaceDir.getPath());
if (workspacePathId == ErrorCode.BAD_PATH) {
throw new FatalBuildStoreError("Unable to add initial \"workspace\" directory.");
}
pkgRootMgr.setWorkspaceRoot(workspacePathId);
pkgRootMgr.setBuildMLFileDepth(0);
}
/*
* In schema version 404, we added the "Main" package by default. If this doesn't exist
* yet, then add it.
*/
if (packages.getId("Main") == ErrorCode.NOT_FOUND) {
packages.addPackage("Main");
}
}
/*-------------------------------------------------------------------------------------*/
/**
* Open an existing BuildStore database. Clients should not invoke this constructor
* directly. Instead, use BuildStoreFactory.
*
* @param buildStoreName Name of the database to open or create.
* @param saveRequired True if BuildStore must explicitly be "saved" before
* it's closed (otherwise the changes will be discarded).
* @throws FileNotFoundException The database file can't be found, or isn't writable.
* @throws IOException An I/O problem occurred while opening the database file.
* @throws BuildStoreVersionException The database schema of an existing database is the wrong version.
*/
public BuildStore(String buildStoreName, boolean saveRequired)
throws FileNotFoundException, IOException, BuildStoreVersionException {
this(buildStoreName, saveRequired, false);
}
/*-------------------------------------------------------------------------------------*/
/**
* Open an existing BuildStore database. Clients should not invoke this constructor
* directly. Instead, use BuildStoreFactory instead.
*
* @param buildStoreName Name of the database to open or create.
* @throws FileNotFoundException The database file can't be found, or isn't writable.
* @throws IOException An I/O problem occurred while opening the database file.
* @throws BuildStoreVersionException The database schema of an existing database is the
* wrong version.
*/
public BuildStore(String buildStoreName)
throws FileNotFoundException, IOException, BuildStoreVersionException {
this(buildStoreName, false, false);
}
/*=====================================================================================*
* PUBLIC METHODS
*=====================================================================================*/
/* (non-Javadoc)
* @see com.buildml.model.IBuildStore#getBuildStoreVersion()
*/
@Override
public int getBuildStoreVersion() {
return db.getBuildStoreVersion();
}
/*-------------------------------------------------------------------------------------*/
/* (non-Javadoc)
* @see com.buildml.model.IBuildStore#getFileMgr()
*/
@Override
public IFileMgr getFileMgr() {
return fileMgr;
}
/*-------------------------------------------------------------------------------------*/
/* (non-Javadoc)
* @see com.buildml.model.IBuildStore#getFileGroupMgr()
*/
@Override
public IFileGroupMgr getFileGroupMgr() {
return fileGroupMgr;
}
/*-------------------------------------------------------------------------------------*/
/* (non-Javadoc)
* @see com.buildml.model.IBuildStore#getFileIncludeMgr()
*/
@Override
public IFileIncludeMgr getFileIncludeMgr() {
return fileIncludeMgr;
}
/*-------------------------------------------------------------------------------------*/
/* (non-Javadoc)
* @see com.buildml.model.IBuildStore#getActionMgr()
*/
@Override
public IActionMgr getActionMgr() {
return actionMgr;
}
/*-------------------------------------------------------------------------------------*/
/* (non-Javadoc)
* @see com.buildml.model.IBuildStore#getReportMgr()
*/
@Override
public IReportMgr getReportMgr() {
return reportMgr;
}
/*-------------------------------------------------------------------------------------*/
/* (non-Javadoc)
* @see com.buildml.model.IBuildStore#getFileAttributeMgr()
*/
@Override
public IFileAttributeMgr getFileAttributeMgr() {
return fileAttrMgr;
}
/*-------------------------------------------------------------------------------------*/
/* (non-Javadoc)
* @see com.buildml.model.IBuildStore#getPackageMgr()
*/
@Override
public IPackageMgr getPackageMgr() {
return packages;
}
/*-------------------------------------------------------------------------------------*/
/* (non-Javadoc)
* @see com.buildml.model.IBuildStore#getPackageMemberMgr()
*/
@Override
public IPackageMemberMgr getPackageMemberMgr() {
return pkgMemberMgr;
}
/*-------------------------------------------------------------------------------------*/
/* (non-Javadoc)
* @see com.buildml.model.IBuildStore#getPackageRootMgr()
*/
@Override
public IPackageRootMgr getPackageRootMgr() {
return pkgRootMgr;
}
/*-------------------------------------------------------------------------------------*/
/* (non-Javadoc)
* @see com.buildml.model.IBuildStore#getActionTypeMgr()
*/
@Override
public IActionTypeMgr getActionTypeMgr() {
return actionTypeMgr;
}
/*-------------------------------------------------------------------------------------*/
/* (non-Javadoc)
* @see com.buildml.model.IBuildStore#getSubPackageMgr()
*/
@Override
public ISubPackageMgr getSubPackageMgr() {
return subPkgMgr;
}
/*-------------------------------------------------------------------------------------*/
/**
* @return The SlotMgr associated with this BuildStore.
*/
public SlotMgr getSlotMgr() {
return slotMgr;
}
/*-------------------------------------------------------------------------------------*/
/* (non-Javadoc)
* @see com.buildml.model.IBuildStore#setFastAccessMode(boolean)
*/
@Override
public boolean setFastAccessMode(boolean fast) {
return db.setFastAccessMode(fast);
}
/*-------------------------------------------------------------------------------------*/
/* (non-Javadoc)
* @see com.buildml.model.IBuildStore#close()
*/
@Override
public void close() {
db.close();
}
/*-------------------------------------------------------------------------------------*/
/* (non-Javadoc)
* @see com.buildml.model.IBuildStore#save()
*/
@Override
public void save() throws IOException
{
db.save();
}
/*-------------------------------------------------------------------------------------*/
/* (non-Javadoc)
* @see com.buildml.model.IBuildStore#saveAs(java.lang.String)
*/
@Override
public void saveAs(String fileToSave) throws IOException
{
/* if we're changing the saveAs path, we might need to change the workspace depth */
String workspaceRoot = pkgRootMgr.getWorkspaceRootNative();
if (!fileToSave.startsWith(workspaceRoot + "/")) {
throw new IOException("Can not save file outside of workspace");
}
int newDepth = 0;
File parentFile = new File(fileToSave).getParentFile();
while (!(parentFile.toString().equals(workspaceRoot))) {
parentFile = parentFile.getParentFile();
newDepth++;
}
pkgRootMgr.setBuildMLFileDepth(newDepth);
/* proceed to save the database in the new location */
db.saveAs(fileToSave);
}
/*-------------------------------------------------------------------------------------*/
/* (non-Javadoc)
* @see com.buildml.model.IFileMgr#emptyTrash()
*/
public void emptyTrash() {
db.emptyTrash();
}
/*=====================================================================================*
* PACKAGE-LEVEL METHODS
*=====================================================================================*/
/**
* Fetch a reference to this BuildStore's underlying database. This is a package-scope
* method to be used only by delegate classes (such as FileMgr).
*
* @return Reference to this BuildStore's underlying database.
*/
/* package */ BuildStoreDB getBuildStoreDB() {
return db;
}
/*-------------------------------------------------------------------------------------*/
}