/*****************************************************************************
* All public interface based on Starteam API are a property of Borland,
* those interface are reproduced here only for testing purpose. You should
* never use those interface to create a competitive product to the Starteam
* Server.
*
* The implementation is given AS-IS and should not be considered a reference
* to the API. The behavior on a lots of method and class will not be the
* same as the real API. The reproduction only seek to mimic some basic
* operation. You will not found anything here that can be deduced by using
* the real API.
*
* Fake-Starteam is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*****************************************************************************/
package com.starbase.starteam;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Properties;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import org.ossnoize.fakestarteam.FakeFolder;
import org.ossnoize.fakestarteam.FileUtility;
import org.ossnoize.fakestarteam.InternalPropertiesProvider;
import org.ossnoize.fakestarteam.SimpleTypedResourceIDProvider;
import org.ossnoize.fakestarteam.exception.InvalidOperationException;
import org.ossnoize.fakestarteam.exception.ObjectIdNotFoundError;
import com.starbase.util.MD5;
import com.starbase.util.OLEDate;
public class File extends Item {
private static final String FILE_STORED = "stored.gz";
public File(Folder parent) {
super();
this.parent = parent;
this.view = parent.getView();
isNew = true;
}
protected File(int id, View view) {
super();
this.view = view;
try {
holdingPlace = createHoldingPlace(id, findRightRevision(id));
if(!holdingPlace.exists())
throw new ObjectIdNotFoundError(id);
} catch (IOException e) {
throw new InvalidOperationException("Cannot initialize the " + id + " in " + parent);
}
loadProperties();
}
protected File(int id, int revision, View view) {
super();
this.view = view;
try {
holdingPlace = createHoldingPlace(id, revision);
if(!holdingPlace.exists())
throw new ObjectIdNotFoundError(id);
} catch (IOException e) {
throw new InvalidOperationException("Cannot initialize the " + id + " in " + parent + ": revision " + revision);
}
loadProperties();
}
public void add(java.io.File file, String name, String desc, String reason, int lockStatus, boolean updateStatus) throws java.io.IOException {
addFromStream(new FileInputStream(file), name, desc, reason, lockStatus);
}
public void addFromStream(InputStream stream, String fileName, String description, String comment, int lockStatus) throws java.io.IOException {
if(isNew()) {
registerNewID();
holdingPlace = createHoldingPlace(0);
//loadProperties();
setRevisionNumber(0);
itemProperties.setProperty(propertyKeys.FILE_CONTENT_REVISION, Integer.toString(1));
setComment(comment);
setDescription(description);
setName(fileName);
setModifiedBy();
setCreatedTime();
setModifiedTime();
copyToGz(stream);
isNew = false;
update();
shareTo(parent);
} else {
throw new InvalidOperationException("Cannot add a file that is already existing");
}
}
private java.io.File createHoldingPlace(int revision) throws IOException {
return createHoldingPlace(getObjectID(), revision);
}
private java.io.File createHoldingPlace(OLEDate date) throws IOException {
java.io.File storage = InternalPropertiesProvider.getInstance().getStorageLocation();
java.io.File folder = new java.io.File(storage.getCanonicalPath() + java.io.File.separator + getObjectID());
int lowestVersion = Integer.MAX_VALUE;
for(java.io.File version : folder.listFiles()) {
int nbVersion = Integer.parseInt(version.getName());
Properties props = new Properties();
java.io.File versionProperties = new java.io.File(version.getCanonicalPath() + java.io.File.separator + FILE_PROPERTIES);
FileInputStream versionStream = new FileInputStream(versionProperties);
props.load(versionStream);
versionStream.close();
long time = Long.parseLong(props.getProperty(propertyKeys.MODIFIED_TIME));
if(time <= date.getLongValue() && nbVersion < lowestVersion) {
lowestVersion = nbVersion;
}
}
return createHoldingPlace(lowestVersion);
}
private java.io.File createHoldingPlace(int id, int revision) throws IOException {
java.io.File storage = InternalPropertiesProvider.getInstance().getStorageLocation();
String folder = storage.getCanonicalPath() + java.io.File.separator + id + java.io.File.separator + revision;
return new java.io.File(folder);
}
public void checkinFromStream(InputStream stream, String reason, int lockStatus, boolean eol) throws java.io.IOException {
if(!isNew() && !isFromHistory()) {
int newRevision = getRevisionNumber() + 1;
loadProperties();
holdingPlace = createHoldingPlace(newRevision);
setRevisionNumber(newRevision);
itemProperties.setProperty(propertyKeys.FILE_CONTENT_REVISION, Integer.toString(getContentVersion() + 1));
setComment(reason);
setModifiedBy();
setModifiedTime();
copyToGz(stream);
update();
} else {
throw new InvalidOperationException("Cannot check-in a file that was not added or from history");
}
}
public void checkinFrom(java.io.File file, String reason, int lockStatus, boolean forceCheckin, boolean updateStatus) throws java.io.IOException {
if(!isNew()) {
int newRevision = getRevisionNumber() + 1;
loadProperties();
holdingPlace = createHoldingPlace(newRevision);
if(holdingPlace.exists()) {
if(forceCheckin && isFromHistory()) {
newRevision = findLastRevision(getObjectID()) + 1;
holdingPlace = createHoldingPlace(newRevision);
} else {
throw new InvalidOperationException("Cannot check-in a past version of a file, do a force check-in to force the update");
}
}
setRevisionNumber(newRevision);
//TODO: May need some rework since the file from history may still need a new content version.
itemProperties.setProperty(propertyKeys.FILE_CONTENT_REVISION, Integer.toString(getContentVersion() + 1));
setComment(reason);
setModifiedBy();
setModifiedTime();
copyToGz(file);
update();
} else {
throw new InvalidOperationException("Cannot check-in a file that was not added or from history");
}
}
public boolean checkoutByVersion(java.io.File checkoutTo, int viewVersion, int lockStatus, boolean timeStampNow, boolean eol, boolean updateStatus) throws java.io.IOException {
holdingPlace = createHoldingPlace(viewVersion);
if(holdingPlace.exists()) {
loadProperties();
if(null == checkoutTo) {
//TODO: build the default checkout directory location
throw new NullPointerException("Does not yet support null checkoutTo parameter");
}
copyFromGz(holdingPlace, checkoutTo);
if(!timeStampNow) {
checkoutTo.setLastModified(getModifiedTime().getLongValue());
}
return true;
} else {
return false;
}
}
public void checkoutByDate(java.io.File checkoutTo, OLEDate date, int lockStatus, boolean timeStampNow, boolean eol, boolean updateStatus) throws java.io.IOException {
holdingPlace = createHoldingPlace(date);
if(holdingPlace.exists()) {
if(null == checkoutTo) {
throw new NullPointerException("Does not yet support null checkoutTo parameter");
}
copyFromGz(holdingPlace, checkoutTo);
if(!timeStampNow) {
checkoutTo.setLastModified(getModifiedTime().getLongValue());
}
} else {
throw new InvalidOperationException("The file does not exist in the repository");
}
}
public void checkoutByLabelID(java.io.File aFile, int checkoutLabelID, int lockType, boolean timeStampNow, boolean eolConversionEnabled, boolean updateStatus) throws java.io.IOException {
Label theLabel = new Label(getView().getID(), checkoutLabelID);
int viewVersion = theLabel.getRevisionOfItem(getItemID());
checkoutByVersion(aFile, viewVersion, lockType, timeStampNow, eolConversionEnabled, updateStatus);
}
public void checkoutTo(java.io.File checkoutTo, int lockStatus, boolean timeStampNow, boolean eol, boolean updateStatus) throws java.io.IOException {
if(holdingPlace.exists()) {
copyFromGz(holdingPlace, checkoutTo);
if(!timeStampNow) {
checkoutTo.setLastModified(getModifiedTime().getLongValue());
}
} else {
throw new InvalidOperationException("The file does not exist in the repository");
}
}
private void copyToGz(java.io.File file) throws IOException {
copyToGz(new FileInputStream(file));
}
private void copyToGz(InputStream stream) throws IOException {
MessageDigest digest;
int encoding = propertyEnums.FILE_ENCODING_ASCII;
try {
digest = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
throw new IOException("Could not find digest type MD5: " + e.getMessage());
}
GZIPOutputStream gzout = null;
FileOutputStream fout = null;
InputStream fin = null;
if(!holdingPlace.exists())
holdingPlace.mkdirs();
fout = new FileOutputStream(holdingPlace.getCanonicalPath() + java.io.File.separator + FILE_STORED);
gzout = new GZIPOutputStream(fout);
fin = stream;
byte[] buffer = new byte[1024*64];
int read = fin.read(buffer);
long size = 0;
while(read >= 0) {
size += read;
gzout.write(buffer, 0, read);
digest.update(buffer, 0, read);
// Analyze the encoding of the file.
if(encoding == propertyEnums.FILE_ENCODING_ASCII ||
encoding == propertyEnums.FILE_ENCODING_UNICODE)
{
for(int it = 0; it < read; ++it) {
if(0 == buffer[it]) {
encoding = propertyEnums.FILE_ENCODING_BINARY;
break;
} else if (0 > buffer[it]) {
encoding = propertyEnums.FILE_ENCODING_UNICODE;
}
}
}
read = fin.read(buffer);
}
byte[] md5Array = digest.digest();
MD5 fileChecksum = new MD5(md5Array);
itemProperties.setProperty(propertyKeys.FILE_MD5_CHECKSUM, fileChecksum.toHexString());
itemProperties.setProperty(propertyKeys.FILE_SIZE, Long.toString(size));
itemProperties.setProperty(propertyKeys.FILE_ENCODING, Integer.toString(encoding));
FileUtility.close(fin, gzout, fout);
}
private void copyFromGz(java.io.File source, java.io.File target) throws IOException {
GZIPInputStream gzin = null;
FileInputStream fin = null;
FileOutputStream fout = null;
if(!source.exists()) {
throw new InvalidOperationException("Could not find the storing folder");
}
fin = new FileInputStream(source.getCanonicalPath() + java.io.File.separator + FILE_STORED);
gzin = new GZIPInputStream(fin);
fout = new FileOutputStream(target);
FileUtility.copyStream(fout, gzin);
FileUtility.close(fout, gzin, fin);
}
public void setDescription(String description) {
if(null != itemProperties) {
itemProperties.setProperty(propertyKeys.FILE_DESCRIPTION, description);
} else {
throw new InvalidOperationException("Item properties are not initialized");
}
}
public String getDescription() {
if(null != itemProperties) {
return itemProperties.getProperty(propertyKeys.FILE_DESCRIPTION);
} else {
throw new InvalidOperationException("Item properties are not initialized");
}
}
public void setName(String name) {
if(null != itemProperties) {
itemProperties.setProperty(propertyKeys.FILE_NAME, name);
} else {
throw new InvalidOperationException("Item properties are not initialized");
}
}
public String getName() {
if(null != itemProperties) {
return itemProperties.getProperty(propertyKeys.FILE_NAME);
} else {
throw new InvalidOperationException("Item properties are not initialized");
}
}
public String getFullName() {
if(null != itemProperties) {
return itemProperties.getProperty(propertyKeys.FOLDER_PATH)
+ java.io.File.pathSeparator
+ itemProperties.getProperty(propertyKeys.FILE_NAME);
} else {
throw new InvalidOperationException("Item properties are not initialized");
}
}
public long getSizeEx() {
if(null != itemProperties) {
try {
return Long.parseLong(itemProperties.getProperty(propertyKeys.FILE_SIZE));
} catch (NumberFormatException e) {
return -1L;
}
} else {
throw new InvalidOperationException("Item properties are not initialized");
}
}
@Override
public void update() {
if(itemProperties == null) {
throw new InvalidOperationException("Properties are not initialized yet!!!");
}
FileOutputStream fout = null;
try {
if(!holdingPlace.exists())
holdingPlace.mkdirs();
fout = new FileOutputStream(holdingPlace.getCanonicalPath() + java.io.File.separator + FILE_PROPERTIES);
itemProperties.store(fout, "File properties");
} catch (IOException e) {
e.printStackTrace();
} finally {
FileUtility.close(fout);
}
}
@Override
protected void loadProperties() {
if(null == itemProperties) {
itemProperties = new Properties();
}
FileInputStream fin = null;
try {
java.io.File fileProperty = new java.io.File(holdingPlace.getCanonicalPath() + java.io.File.separator + FILE_PROPERTIES);
if(fileProperty.exists()) {
fin = new FileInputStream(fileProperty);
itemProperties.load(fin);
int id = Integer.parseInt(itemProperties.getProperty(propertyKeys.OBJECT_ID));
int viewid = Integer.parseInt(itemProperties.getProperty(propertyKeys._VIEW_ID));
if(viewid != view.getID()) {
//TODO: When branching is figured out, verify that the file is from the parent.
throw new InvalidOperationException("This file is not part of the requested view");
}
SimpleTypedResourceIDProvider.getProvider().registerExisting(view, id, this);
if(null == parent) {
SimpleTypedResource ressource = SimpleTypedResourceIDProvider.getProvider().findExisting(view, getParentObjectID());
if(ressource instanceof Folder) {
parent = (Folder)ressource;
} else if (0 != getParentObjectID()) {
parent = new FakeFolder(this.view, getParentObjectID(), null);
} else {
throw new InvalidOperationException("Got an invalid parent id " + getParentObjectID() + " for the file object id " + id);
}
}
} else {
throw new InvalidOperationException("Could not find the properties in the storage location: " + holdingPlace);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
FileUtility.close(fin);
}
}
private void registerNewID() {
itemProperties = new Properties();
itemProperties.setProperty(propertyKeys.OBJECT_ID,
Integer.toString(SimpleTypedResourceIDProvider.getProvider().registerNew(view, this)));
itemProperties.setProperty(propertyKeys.PARENT_OBJECT_ID,
Integer.toString(parent.getObjectID()));
itemProperties.setProperty(propertyKeys._VIEW_ID, Integer.toString(view.getID()));
}
@Override
protected List<Item> loadHistory() {
List<Item> ret = new ArrayList<Item>(getRevisionNumber());
for(int i=0; i < getRevisionNumber(); i++) {
ret.add(new File(getObjectID(), i, getView()));
}
Collections.reverse(ret);
return ret;
}
@Override
public String toString() {
return getName();
}
public int getStatus() throws IOException {
return getStatus(getWorkingFile());
}
public int getStatus(java.io.File file) throws IOException {
OLEDate time = getModifiedTime();
if(!file.isFile()) {
return Status.MISSING;
}
long lastModificed = file.lastModified();
if(lastModificed > time.getLongValue()) {
return Status.MODIFIED;
}
long size = getSizeEx();
if(lastModificed == time.getLongValue()) {
if(file.length() == size) {
return Status.CURRENT;
}
}
if(lastModificed < time.getLongValue()) {
return Status.OUTOFDATE;
}
return Status.UNKNOWN;
}
public int getStatusNow() throws IOException {
return getStatus();
}
private java.io.File getWorkingFile() {
return new java.io.File(System.getProperty("user.dir") + java.io.File.separator + getParentFolderHierarchy() + java.io.File.separator + getName());
}
public int getStatusByMD5(MD5 md5) {
String md5sum = itemProperties.getProperty(propertyKeys.FILE_MD5_CHECKSUM);
if(null != md5sum) {
if(md5sum.equalsIgnoreCase(md5.toHexString())) {
return Status.CURRENT;
}
}
return Status.UNKNOWN;
}
public byte[] getMD5() {
if(null != itemProperties) {
String md5sum = itemProperties.getProperty(propertyKeys.FILE_MD5_CHECKSUM);
MD5 fileChecksum = new MD5(md5sum);
return fileChecksum.getData();
}
throw new InvalidOperationException("Item Properties was never initialized");
}
public int getCharset() {
if(null != itemProperties) {
if(itemProperties.containsKey(propertyKeys.FILE_ENCODING)) {
try {
return Integer.parseInt(itemProperties.getProperty(propertyKeys.FILE_ENCODING));
} catch (NumberFormatException ex) {
throw new InvalidOperationException("The file encoding value is invalid: " + ex.getMessage());
}
} else {
// Assume file that have unidentified encoding as binary.
return propertyEnums.FILE_ENCODING_BINARY;
}
} else {
throw new InvalidOperationException("Item Properties was never initialized");
}
}
@Override
public Item shareTo(Folder folder) {
StringBuffer childIdList = null;
if(folder.itemProperties.containsKey(propertyKeys._FILES)) {
childIdList = new StringBuffer(folder.itemProperties.getProperty(propertyKeys._FILES)).append(";");
} else {
childIdList = new StringBuffer(25);
}
childIdList.append(getObjectID());
folder.itemProperties.setProperty(propertyKeys._FILES, childIdList.toString());
folder.update();
return super.shareTo(folder);
}
@Override
public void moveTo(Folder folder) {
Folder origin = getParentFolder();
String thisStringId = Integer.toString(getObjectID());
if(origin.itemProperties.containsKey(propertyKeys._FILES)) {
StringBuffer idList = new StringBuffer(origin.itemProperties.getProperty(propertyKeys._FILES));
int start = idList.indexOf(thisStringId);
idList.delete(start, start+thisStringId.length());
origin.itemProperties.setProperty(propertyKeys._FILES, idList.toString());
origin.update();
}
super.moveTo(folder);
}
@Override
public Type getType() {
return new Type(getTypeNames().FILE, view.getServer());
}
public int getContentVersion() {
int revision = 1;
if(itemProperties.containsKey(propertyKeys.FILE_CONTENT_REVISION)) {
revision = Integer.parseInt(itemProperties.getProperty(propertyKeys.FILE_CONTENT_REVISION));
} else {
revision = getRevisionNumber();
}
return revision;
}
@Override
protected int getObjectSpecificVersion() {
return getContentVersion();
}
}