/*
* (c) Copyright 2010-2011 AgileBirds
*
* This file is part of OpenFlexo.
*
* OpenFlexo is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* OpenFlexo 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. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenFlexo. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.openflexo.foundation.cg;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.openflexo.foundation.Format;
import org.openflexo.foundation.TargetType;
import org.openflexo.foundation.cg.dm.CGDataModification;
import org.openflexo.foundation.cg.dm.CGFileCreated;
import org.openflexo.foundation.cg.dm.CGFileDeleted;
import org.openflexo.foundation.cg.dm.CGReleaseRegistered;
import org.openflexo.foundation.cg.dm.CGRepositoryConnected;
import org.openflexo.foundation.cg.dm.CGRepositoryDisconnected;
import org.openflexo.foundation.cg.dm.CGStructureRefreshed;
import org.openflexo.foundation.cg.dm.CustomTemplateRepositoryChanged;
import org.openflexo.foundation.cg.dm.LogAdded;
import org.openflexo.foundation.cg.dm.LongOperationStarted;
import org.openflexo.foundation.cg.dm.LongOperationStopped;
import org.openflexo.foundation.cg.templates.CustomCGTemplateRepository;
import org.openflexo.foundation.cg.version.CGRelease;
import org.openflexo.foundation.cg.version.CGVersionIdentifier;
import org.openflexo.foundation.rm.FlexoResource;
import org.openflexo.foundation.rm.FlexoResourceData;
import org.openflexo.foundation.rm.FlexoXMLStorageResource.SaveXMLResourceException;
import org.openflexo.foundation.rm.ProjectExternalRepository;
import org.openflexo.foundation.rm.ProjectRestructuration;
import org.openflexo.foundation.rm.SaveResourceException;
import org.openflexo.foundation.rm.SaveResourcePermissionDeniedException;
import org.openflexo.foundation.rm.cg.CGRepositoryFileResource;
import org.openflexo.foundation.rm.cg.GenerationStatus;
import org.openflexo.foundation.utils.FlexoProgress;
import org.openflexo.foundation.utils.FlexoProjectFile;
import org.openflexo.foundation.xml.GeneratedCodeBuilder;
import org.openflexo.localization.FlexoLocalization;
public abstract class GenerationRepository extends CGObject {
private static final Logger logger = Logger.getLogger(GenerationRepository.class.getPackage().getName());
private ProjectExternalRepository _sourceCodeRepository;
private Hashtable<String, CGSymbolicDirectory> _symbolicDirectories;
private Vector<CGFile> _files;
private boolean _manageHistory = true;
private String _displayName;
private Vector<CGRelease> _releases;
private Object projectGenerator;
private CGVersionIdentifier DEFAULT_VERSION_ID = CGVersionIdentifier.DEFAULT_VERSION_ID();
/**
* Create a new GeneratedCodeRepository.
*/
public GenerationRepository(GeneratedCodeBuilder builder) {
this(builder.generatedCode);
initializeDeserialization(builder);
}
public GenerationRepository(GeneratedOutput generatedCode) {
super(generatedCode);
_symbolicDirectories = new Hashtable<String, CGSymbolicDirectory>();
_files = new Vector<CGFile>();
_releases = new Vector<CGRelease>();
}
public GenerationRepository(GeneratedOutput generatedCode, String name, File directory) throws DuplicateCodeRepositoryNameException {
this(generatedCode);
if (getProject().getExternalRepositoryWithKey(name) != null) {
throw new DuplicateCodeRepositoryNameException(this, name);
}
if (!directory.exists()) {
directory.mkdirs();
}
_sourceCodeRepository = getProject().setDirectoryForRepositoryName(name, directory);
}
/**
* Overrides finalizeDeserialization
*
* @see org.openflexo.foundation.FlexoXMLSerializableObject#finalizeDeserialization(java.lang.Object)
*/
@Override
public void finalizeDeserialization(Object builder) {
super.finalizeDeserialization(builder);
for (CGFile file : _files) {
if (file.getResource() == null) {
file.delete();
}
}
}
@Override
public String getFullyQualifiedName() {
return (getGeneratedCode() != null ? getGeneratedCode().getFullyQualifiedName() : "null") + "." + getName();
}
@Override
public String getName() {
return getSourceCodeRepository().getIdentifier();
}
@Override
public void setName(String name) throws DuplicateCodeRepositoryNameException {
if (_sourceCodeRepository != null) {
if (name != null && !name.equals(getName())) {
throw new IllegalStateException("External repository identifier cannot change !");
// if (getProject().getExternalRepositoryWithKey(name) != null) {
// throw new DuplicateCodeRepositoryNameException(this,name);
// }
// _sourceCodeRepository.setIdentifier(name);
}
} else {
_sourceCodeRepository = getProject().getExternalRepositoryWithKey(name);
}
}
public String getDisplayName() {
if (_displayName == null) {
_displayName = getName();
}
return _displayName;
}
public void setDisplayName(String displayName) {
String oldValue = this._displayName;
_displayName = displayName;
setChanged();
notifyObservers(new CGDataModification("displayName", oldValue, displayName));
}
public File getDirectory() {
return getSourceCodeRepository().getDirectory();
}
public void setDirectory(File aDirectory) {
File oldValue = getSourceCodeRepository().getDirectory();
getSourceCodeRepository().setDirectory(aDirectory);
connect();
setChanged();
notifyObservers(new CGDataModification("directory", oldValue, aDirectory));
}
public void delete(boolean deleteFiles) {
delete(null, deleteFiles);
}
public void delete(FlexoProgress progress, boolean deleteFiles) {
Vector<CGFile> files = (Vector<CGFile>) getFiles().clone();
for (CGFile file : files) {
if (progress != null) {
progress.setProgress(FlexoLocalization.localizedForKey("deleting") + " " + file.getFileName());
}
file.delete(deleteFiles);
}
if (deleteFiles) {
getProject().addToFilesToDelete(getCodeGenerationWorkingDirectory());
}
if (deleteFiles) {
getProject().deleteFilesToBeDeleted();
}
deleteExternalRepositories();
// GPO: the following loop attempts to remove any left-behind resource of this repository.
// The purpose of doing this is to avoid generated resources from being without a proper CGFile
// causing them to irremediably be unusable
for (FlexoResource<? extends FlexoResourceData> r : new ArrayList<FlexoResource<? extends FlexoResourceData>>(getProject()
.getResources().values())) {
if (r instanceof CGRepositoryFileResource) {
CGRepositoryFileResource file = (CGRepositoryFileResource) r;
if (file.getCGFile() == null) {
if (file.getName() != null && file.getName().indexOf('.') > -1) {
if (file.getName().substring(0, file.getName().indexOf('.')).equals(getName())) {
if (logger.isLoggable(Level.WARNING)) {
logger.warning("Found a resource without CGFile that is supposed to be in this repository: "
+ file.getFullyQualifiedName() + " I will delete it now");
}
file.delete(deleteFiles);
}
}
}
}
}
try {
getProject().getFlexoRMResource().saveResourceData();
} catch (SaveXMLResourceException e) {
e.printStackTrace();
logger.warning("Unexpected " + e);
} catch (SaveResourcePermissionDeniedException e) {
e.printStackTrace();
logger.warning("Unexpected " + e);
} catch (SaveResourceException e) {
e.printStackTrace();
logger.warning("Unexpected " + e);
}
// Too dangerous (if for example, the repository points to '/' or 'c:\' the consequences could be disatrous
/*
* if (deleteFiles) // Really delete files on disk FileUtils.recursiveDeleteFile(getDirectory());
*/
// rebuildStructure();
super.delete();
getGeneratedCode().removeFromGeneratedRepositories(this);
projectGenerator = null;
}
protected void deleteExternalRepositories() {
if (getSourceCodeRepository() != null) {
getProject().removeFromExternalRepositories(getSourceCodeRepository());
}
}
public CGSymbolicDirectory getSymbolicDirectory(CGFile file) {
if (this == file.getRepository()) {
String filePath = file.getResource().getResourceFile().getRelativePath();
Enumeration<CGSymbolicDirectory> en = _symbolicDirectories.elements();
while (en.hasMoreElements()) {
CGSymbolicDirectory reply = en.nextElement();
if (filePath.indexOf(reply.getDirectory().getRelativePath()) > -1) {
return reply;
}
}
}
return null;
}
public abstract TargetType getTarget();
public abstract Format getFormat();
public abstract void setFormat(Format format);
public synchronized Hashtable<String, CGSymbolicDirectory> getSymbolicDirectories() {
return _symbolicDirectories;
}
public void setSymbolicDirectories(Hashtable<String, CGSymbolicDirectory> symbolicDirectories) {
_symbolicDirectories = symbolicDirectories;
}
public void setSymbolicDirectoryForKey(CGSymbolicDirectory dir, String name) {
dir.setName(name);
dir.setGeneratedCodeRepository(this);
_symbolicDirectories.put(name, dir);
setChanged();
notifyObservers(new CGDataModification("symbolicDirectories", null, dir));
}
public void removeSymbolicDirectoryWithKey(String name) {
CGSymbolicDirectory old = _symbolicDirectories.get(name);
if (old != null) {
old.setGeneratedCodeRepository(null);
}
_symbolicDirectories.remove(name);
setChanged();
notifyObservers(new CGDataModification("symbolicDirectories", old, null));
}
public CGSymbolicDirectory getSymbolicDirectoryNamed(String name) {
return getSymbolicDirectoryNamed(name, true);
}
public CGSymbolicDirectory getSymbolicDirectoryNamed(String name, boolean createIfMissing) {
CGSymbolicDirectory returned = _symbolicDirectories.get(name);
if (returned == null && createIfMissing) {
returned = new CGSymbolicDirectory(this, name, new FlexoProjectFile(getProject(), getSourceCodeRepository(), "/."));
setSymbolicDirectoryForKey(returned, name);
returned.getDirectory().getFile().mkdirs();
}
return returned;
}
public ProjectExternalRepository getSourceCodeRepository() {
if (_sourceCodeRepository == null) {
if (_displayName != null) {
_sourceCodeRepository = getProject().getExternalRepositoryWithKey(_displayName);
}
if (_sourceCodeRepository == null) {
_sourceCodeRepository = getProject().setDirectoryForRepositoryName(_displayName != null ? _displayName : "Default",
getDefaultSourceDirectory());
}
}
return _sourceCodeRepository;
}
public File getDefaultSourceDirectory() {
return new File(System.getProperty("user.home") + "/" + getProject().getProjectName() + "/" + getTarget().getName());
}
@Override
public boolean isEnabled() {
return isConnected && getSourceCodeRepository().isConnected();
}
public boolean isConnected() {
if (!connectionStatusInitialized) {
connectionStatusInitialized = true;
isConnected = getSourceCodeRepository() != null && getSourceCodeRepository().isConnected();
}
return isConnected;
}
private boolean isConnected = true;
private boolean connectionStatusInitialized = false;
public boolean connect() {
if (getSourceCodeRepository() != null) {
if (!getSourceCodeRepository().isConnected()) {
getSourceCodeRepository().setDirectory(getSourceCodeRepository().getDirectory());
}
if (getSourceCodeRepository().isConnected()) {
isConnected = true;
for (CGFile file : getFiles()) {
if (file.getResource() != null) {
file.getResource().activate();
}
}
setChanged();
notifyObservers(new CGRepositoryConnected(this));
return true;
}
}
// Cannot connect: repository directory is not valid
return false;
}
public void disconnect() {
isConnected = false;
for (CGFile file : getFiles()) {
if (file.getResource() != null) {
file.getResource().deactivate();
}
}
setChanged();
notifyObservers(new CGRepositoryDisconnected(this));
}
public Vector<CGFile> getFiles() {
return _files;
}
public void setFiles(Vector<CGFile> files) {
_files = files;
setChanged();
notifyObservers(new CGDataModification("files", null, files));
}
public void addToFiles(CGFile aFile) {
if (aFile.getResourceName() == null) {
logger.warning("file: " + aFile + " : cannot add a file with null resource");
} else {
aFile.setRepository(this);
_files.add(aFile);
setChanged();
notifyObservers(new CGFileCreated(aFile));
}
}
public void removeFromFiles(CGFile aFile) {
aFile.setRepository(null);
_files.remove(aFile);
setChanged();
notifyObservers(new CGFileDeleted(aFile));
}
@Override
public boolean hasGenerationErrors() {
return hasGenerationErrors;
}
@Override
public boolean needsRegeneration() {
return needsRegeneration;
}
@Override
public boolean needsModelReinjection() {
return needsModelReinjection;
}
@Override
public GenerationStatus getGenerationStatus() {
return generationStatus;
}
public void refresh() {
if (logger.isLoggable(Level.FINE)) {
logger.fine("refresh()");
}
rebuildStructure();
setChanged(false);
notifyObservers(new CGStructureRefreshed());
}
private int generationModifiedCount;
private int diskModifiedCount;
private int conflictsCount;
private int needsMemoryGenerationCount;
private int needsModelReinjectionCount;
private int errorsCount;
private volatile boolean isRebuildingStructure = false;
private synchronized void rebuildStructure() {
if (isRebuildingStructure) {
return;
}
if (logger.isLoggable(Level.INFO)) {
logger.info("Re-compute structure for repository " + getName());
}
isRebuildingStructure = true;
hasGenerationErrors = false;
needsRegeneration = false;
needsModelReinjection = false;
generationStatus = GenerationStatus.UpToDate;
generationModifiedCount = 0;
diskModifiedCount = 0;
conflictsCount = 0;
needsMemoryGenerationCount = 0;
needsModelReinjectionCount = 0;
errorsCount = 0;
try {
for (CGSymbolicDirectory symbDir : getSymbolicDirectories().values()) {
symbDir.clearStructure();
}
for (CGFile file : _files) {
if (file.getSymbolicDirectory() != null) {
file.getSymbolicDirectory().addToStructure(file);
}
if (file.hasGenerationErrors()) {
// logger.info("File "+file+" has generation error");
CGPathElement current = file;
while (current.getParent() != null) {
// logger.info("Set object "+current.getParent()+" to
// have generation errors");
((CGObject) current.getParent()).hasGenerationErrors = true;
current = current.getParent();
}
hasGenerationErrors = true;
errorsCount++;
}
if (file.needsRegeneration()) {
CGPathElement current = file;
while (current.getParent() != null) {
((CGObject) current.getParent()).needsRegeneration = true;
current = current.getParent();
}
needsRegeneration = true;
needsMemoryGenerationCount++;
}
if (file instanceof ModelReinjectableFile && ((ModelReinjectableFile) file).needsModelReinjection()) {
CGPathElement current = file;
while (current.getParent() != null) {
((CGObject) current.getParent()).needsModelReinjection = true;
current = current.getParent();
}
needsModelReinjection = true;
needsModelReinjectionCount++;
}
if (file.getGenerationStatus().isGenerationModified()
|| file.getGenerationStatus() == GenerationStatus.ConflictingMarkedAsMerged) {
CGPathElement current = file;
while (current.getParent() != null) {
if (((CGObject) current.getParent()).generationStatus == GenerationStatus.UpToDate) {
((CGObject) current.getParent()).generationStatus = GenerationStatus.GenerationModified;
} else if (((CGObject) current.getParent()).generationStatus == GenerationStatus.DiskModified) {
((CGObject) current.getParent()).generationStatus = GenerationStatus.ConflictingUnMerged;
}
current = current.getParent();
}
if (generationStatus == GenerationStatus.UpToDate) {
generationStatus = GenerationStatus.GenerationModified;
} else if (generationStatus == GenerationStatus.DiskModified) {
generationStatus = GenerationStatus.ConflictingUnMerged;
}
generationModifiedCount++;
}
if (file.getGenerationStatus().isDiskModified()) {
CGPathElement current = file;
while (current.getParent() != null) {
if (((CGObject) current.getParent()).generationStatus == GenerationStatus.UpToDate) {
((CGObject) current.getParent()).generationStatus = GenerationStatus.DiskModified;
} else if (((CGObject) current.getParent()).generationStatus == GenerationStatus.GenerationModified) {
((CGObject) current.getParent()).generationStatus = GenerationStatus.ConflictingUnMerged;
}
current = current.getParent();
}
if (generationStatus == GenerationStatus.UpToDate) {
generationStatus = GenerationStatus.DiskModified;
} else if (generationStatus == GenerationStatus.GenerationModified) {
generationStatus = GenerationStatus.ConflictingUnMerged;
}
diskModifiedCount++;
}
if (file.getGenerationStatus() == GenerationStatus.ConflictingUnMerged) {
CGPathElement current = file;
while (current.getParent() != null) {
((CGObject) current.getParent()).generationStatus = GenerationStatus.ConflictingUnMerged;
current = current.getParent();
}
generationStatus = GenerationStatus.ConflictingUnMerged;
conflictsCount++;
}
}
} finally {
isRebuildingStructure = false;
}
}
public void notifyLogAdded() {
setChanged();
notifyObservers(new LogAdded());
}
public void notifyLongOperationStarted() {
setChanged();
notifyObservers(new LongOperationStarted(this));
}
public void notifyLongOperationStopped() {
setChanged();
notifyObservers(new LongOperationStopped(this));
}
// ==========================================================================
// ========================== Embedding implementation =====================
// ==========================================================================
@Override
public boolean isContainedIn(CGObject obj) {
if (obj instanceof GeneratedOutput) {
return obj == getGeneratedCode();
} else if (obj instanceof GenerationRepository) {
return obj == this;
}
return false;
}
private CustomCGTemplateRepository _preferredTemplateRepository;
private String _preferredTemplateRepositoryName;
public CustomCGTemplateRepository getPreferredTemplateRepository() {
if (_preferredTemplateRepository == null) {
if (!isDeserializing() && _preferredTemplateRepositoryName != null) {
setPreferredTemplateRepositoryName(_preferredTemplateRepositoryName);
}
}
return _preferredTemplateRepository;
}
public void setPreferredTemplateRepository(CustomCGTemplateRepository preferredTemplateRepository) {
CustomCGTemplateRepository old = _preferredTemplateRepository;
if (old == null || old != preferredTemplateRepository) {
_preferredTemplateRepository = preferredTemplateRepository;
_preferredTemplateRepositoryName = preferredTemplateRepository != null ? preferredTemplateRepository.getName() : null;
if (!isDeserializing()) {
setChanged();
notifyObservers(new CustomTemplateRepositoryChanged(old, preferredTemplateRepository));
rebuildStructure();
}
}
}
public String getPreferredTemplateRepositoryName() {
if (_preferredTemplateRepository != null) {
return _preferredTemplateRepository.getName();
}
return null;
}
public void setPreferredTemplateRepositoryName(String aName) {
if (!isDeserializing()) {
CustomCGTemplateRepository newRepository = getGeneratedCode().getTemplates().getCustomCGTemplateRepositoryForName(aName);
if (newRepository == null) {
logger.warning("Could not find template repository named " + aName);
} else {
setPreferredTemplateRepository(newRepository);
}
} else {
_preferredTemplateRepositoryName = aName;
}
}
public void updatePreferredTemplateRepository() {
CustomCGTemplateRepository newRepository = getGeneratedCode().getTemplates().getCustomCGTemplateRepositoryForName(
_preferredTemplateRepositoryName);
if (newRepository != null) {
setPreferredTemplateRepository(newRepository);
}
}
private File _codeGenerationWorkingDirectory;
public File getCodeGenerationWorkingDirectory() {
if (_codeGenerationWorkingDirectory == null) {
_codeGenerationWorkingDirectory = new File(ProjectRestructuration.getExpectedGeneratedCodeDirectory(getProject()
.getProjectDirectory()), getName());
}
return _codeGenerationWorkingDirectory;
}
public boolean getManageHistory() {
return _manageHistory;
}
public void setManageHistory(boolean manageHistory) {
_manageHistory = manageHistory;
}
private boolean _releasesAreSorted = false;
public Vector<CGRelease> getReleases() {
return _releases;
}
public void setReleases(Vector<CGRelease> releases) {
_releases = releases;
_releasesAreSorted = false;
setChanged();
}
public void addToReleases(CGRelease release) {
release.setCGRepository(this);
_releases.add(release);
_releasesAreSorted = false;
setChanged();
notifyObservers(new CGReleaseRegistered(release));
}
public void removeFromReleases(CGRelease release) {
release.setCGRepository(null);
_releases.remove(release);
_releasesAreSorted = false;
setChanged();
}
public CGRelease getLastRelease() {
ensureReleasesAreSorted();
if (_releases.size() > 0) {
return _releases.lastElement();
}
return null;
}
public void ensureReleasesAreSorted() {
if (!_releasesAreSorted) {
Collections.sort(_releases, CGRelease.COMPARATOR);
_releasesAreSorted = true;
}
}
public CGVersionIdentifier getLastReleaseVersionIdentifier() {
if (getLastRelease() != null) {
return getLastRelease().getVersionIdentifier();
}
return DEFAULT_VERSION_ID;
}
public int getConflictsCount() {
return conflictsCount;
}
public int getDiskModifiedCount() {
return diskModifiedCount;
}
public int getErrorsCount() {
return errorsCount;
}
public int getGenerationModifiedCount() {
return generationModifiedCount;
}
public int getNeedsMemoryGenerationCount() {
return needsMemoryGenerationCount;
}
public int getNeedsModelReinjectionCount() {
return needsModelReinjectionCount;
}
@Override
public String toString() {
return getFullyQualifiedName();
}
public Object getProjectGenerator() {
return projectGenerator;
}
public void setProjectGenerator(Object projectGenerator) {
this.projectGenerator = projectGenerator;
}
}