/*
* (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.generator;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Vector;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
import org.openflexo.foundation.DataModification;
import org.openflexo.foundation.FlexoModelObject;
import org.openflexo.foundation.FlexoObservable;
import org.openflexo.foundation.FlexoObserver;
import org.openflexo.foundation.TargetType;
import org.openflexo.foundation.cg.CGFile;
import org.openflexo.foundation.cg.CGSymbolicDirectory;
import org.openflexo.foundation.cg.GenerationRepository;
import org.openflexo.foundation.cg.action.AbstractGCAction;
import org.openflexo.foundation.cg.dm.CustomTemplateRepositoryChanged;
import org.openflexo.foundation.cg.generator.IFlexoResourceGenerator;
import org.openflexo.foundation.cg.templates.CGTemplates;
import org.openflexo.foundation.cg.templates.TemplateFileNotification;
import org.openflexo.foundation.dkv.DKVValidationModel;
import org.openflexo.foundation.dm.DMValidationModel;
import org.openflexo.foundation.ie.IEValidationModel;
import org.openflexo.foundation.rm.FlexoProject;
import org.openflexo.foundation.rm.FlexoResource;
import org.openflexo.foundation.rm.GeneratedResourceData;
import org.openflexo.foundation.rm.cg.CGRepositoryFileResource;
import org.openflexo.foundation.validation.ValidationReport;
import org.openflexo.foundation.wkf.WKFValidationModel;
import org.openflexo.generator.action.ValidateProject;
import org.openflexo.generator.exception.GenerationException;
import org.openflexo.generator.exception.ModelValidationException;
import org.openflexo.generator.exception.PermissionDeniedException;
import org.openflexo.logging.FlexoLoggingFormatter;
import org.openflexo.toolbox.LogListener;
import org.openflexo.velocity.FlexoVelocity;
public abstract class AbstractProjectGenerator<R extends GenerationRepository> extends MetaGenerator<FlexoProject, R> {
private R repository;
private CGTemplates templates;
private TemplateLocator templateLocator;
private Vector<Generator<? extends FlexoModelObject, R>> generators;
private Vector<Logger> loggers;
private AbstractGCAction action;
public Vector<LogListener> logListeners;
private Handler logHandler;
protected FlexoLoggingFormatter formatter = new FlexoLoggingFormatter();
private boolean handleLogs = false;
public AbstractProjectGenerator(FlexoProject project, R repository) throws GenerationException {
super(null, project);
this.projectGenerator = this;
this.repository = repository;
this.templates = getDefaultTemplates();
this.templateLocator = new TemplateLocator(templates, this);
this.generators = new Vector<Generator<? extends FlexoModelObject, R>>();
this.loggers = new Vector<Logger>();
this.logListeners = new Vector<LogListener>();
this.loggers.add(FlexoVelocity.getLogger());
if (getRootOutputDirectory() != null) {
if (!getRootOutputDirectory().exists()) {
getRootOutputDirectory().mkdirs();
}
if (!getRootOutputDirectory().canWrite()) {
throw new PermissionDeniedException(getRootOutputDirectory(), this);
}
}
if (repository != null) {
for (CGSymbolicDirectory symbDir : repository.getSymbolicDirectories().values()) {
// GPO: I am almost sure that the next block is not required since when writing files to the disk, dirs will be created.
// Meanwhile, I leave this here because it is safer and is pretty much harmless (although not nicely coded)
if (symbDir.getDirectory().getFile() != null) {
symbDir.getDirectory().getFile().mkdirs();
if (!symbDir.getDirectory().getFile().canWrite()) {
throw new PermissionDeniedException(symbDir.getDirectory().getFile(), this);
}
}
}
repository.addObserver(this);
repository.setProjectGenerator(this);
}
}
public abstract CGTemplates getDefaultTemplates();
@Override
public FlexoProject getProject() {
return getObject();
}
@Override
public R getRepository() {
return repository;
}
@Override
public TargetType getTarget() {
if (repository != null) {
return getRepository().getTarget();
}
return getProject().getTargetType();
}
/**
* @return
*/
public File getRootOutputDirectory() {
if (repository != null) {
return repository.getDirectory();
}
return null;
}
@Override
public Vector<CGRepositoryFileResource> refreshConcernedResources(
Vector<CGRepositoryFileResource<? extends GeneratedResourceData, IFlexoResourceGenerator, CGFile>> forResources) {
getRepository().disableObserving();
Vector<CGRepositoryFileResource> returned = super.refreshConcernedResources(forResources);
for (CGFile file : getRepository().getFiles()) {
if (file.getResource() == null) {
file.setMarkedForDeletion(true);
} else {
CGRepositoryFileResource resource = file.getResource();
if (!returned.contains(resource)) {
file.setMarkedForDeletion(true);
}
}
}
getRepository().enableObserving();
getRepository().refresh();
return returned;
}
@Override
public TemplateLocator getTemplateLocator() {
return templateLocator;
}
@Override
public void update(FlexoObservable observable, DataModification dataModification) {
if (dataModification.propertyName() != null
&& (dataModification.propertyName().equals("targetType") || dataModification.propertyName().equals("docType"))) {
getTemplateLocator().notifyTemplateModified();
}
if (dataModification instanceof TemplateFileNotification) {
getTemplateLocator().notifyTemplateModified();
} else if (dataModification instanceof CustomTemplateRepositoryChanged) {
getTemplateLocator().notifyTemplateModified();
}
super.update(observable, dataModification);
}
@Override
public final AbstractGCAction getAction() {
return action;
}
public final void setAction(AbstractGCAction action) {
this.action = action;
}
protected void addToGenerators(Generator<? extends FlexoModelObject, R> generator) {
generators.add(generator);
if (!loggers.contains(generator.getGeneratorLogger())) {
loggers.add(generator.getGeneratorLogger());
}
}
public boolean checkModelConsistency() throws ModelValidationException {
return checkModelConsistency(null, null, null, null);
}
public boolean checkModelConsistency(FlexoObserver ieValidationObserver, FlexoObserver wkfValidationObserver,
FlexoObserver dmValidationObserver, FlexoObserver dkvValidationObserver) throws ModelValidationException {
ModelValidationException thrownException = null;
if (getProject().getFlexoComponentLibrary(false) != null) {
// We validate the component library model
IEValidationModel ieValidationModel = new IEValidationModel(getProject(), getTarget());
if (ieValidationObserver != null) {
ieValidationModel.addObserver(ieValidationObserver);
}
ValidationReport report = getProject().getFlexoComponentLibrary().validate(ieValidationModel);
if (ieValidationObserver != null) {
ieValidationModel.deleteObserver(ieValidationObserver);
}
if (getAction() instanceof ValidateProject) {
((ValidateProject) getAction()).setIeValidationReport(report);
}
if (report.getErrorNb() > 0) {
thrownException = new ModelValidationException("Component library validation failed", "component_library_is_not_valid",
report);
}
}
if (getProject().getFlexoWorkflow(false) != null) {
// We validate the workflow model
WKFValidationModel wkfValidationModel = new WKFValidationModel(getProject(), getTarget());
if (wkfValidationObserver != null) {
wkfValidationModel.addObserver(wkfValidationObserver);
}
ValidationReport report = getProject().getFlexoWorkflow().validate(wkfValidationModel);
if (wkfValidationObserver != null) {
wkfValidationModel.deleteObserver(wkfValidationObserver);
}
if (getAction() instanceof ValidateProject) {
((ValidateProject) getAction()).setWkfValidationReport(report);
}
if (report.getErrorNb() > 0) {
thrownException = new ModelValidationException("Workflow validation failed", "workflow_is_not_valid", report);
}
}
if (getProject().getDKVModel(false) != null) {
// We validate the dkv model
DKVValidationModel dkvValidationModel = new DKVValidationModel(getProject(), getTarget());
if (dkvValidationObserver != null) {
dkvValidationModel.addObserver(dkvValidationObserver);
}
ValidationReport report = getProject().getDKVModel().validate(dkvValidationModel);
if (dkvValidationObserver != null) {
dkvValidationModel.deleteObserver(dkvValidationObserver);
}
if (getAction() instanceof ValidateProject) {
((ValidateProject) getAction()).setDkvValidationReport(report);
}
if (report.getErrorNb() > 0) {
thrownException = new ModelValidationException("DKV validation failed", "domainkeyvalue_is_not_valid", report);
}
}
if (getProject().getDataModel(false) != null) {
DMValidationModel dmValidationModel = new DMValidationModel(getProject(), getTarget());
if (dmValidationObserver != null) {
dmValidationModel.addObserver(dmValidationObserver);
}
ValidationReport report = getProject().getDataModel().validate(dmValidationModel);
if (dmValidationObserver != null) {
dmValidationModel.deleteObserver(dmValidationObserver);
}
if (getAction() instanceof ValidateProject) {
((ValidateProject) getAction()).setDmValidationReport(report);
}
if (report.getErrorNb() > 0) {
thrownException = new ModelValidationException("Data model validation failed", "data_model_is_not_valid", report);
}
if (thrownException != null) {
throw thrownException;
}
}
return true;
}
public void err(String log) {
if (log.matches("\\s*")) {
return;
}
for (LogListener l : logListeners) {
l.err(log);
}
}
public void warn(String log) {
if (log.matches("\\s*")) {
return;
}
for (LogListener l : logListeners) {
l.warn(log);
}
}
@Override
public void log(String log) {
if (log.matches("\\s*")) {
return;
}
for (LogListener l : logListeners) {
l.log(log);
}
}
private Handler getLogHandler() {
if (logHandler == null) {
logHandler = new Handler() {
@Override
public void close() throws SecurityException {
}
@Override
public void flush() {
getRepository().notifyLogAdded();
}
@Override
public void publish(LogRecord record) {
if (record.getLevel().intValue() > Level.INFO.intValue()) {
err(formatter.format(record));
} else {
log(formatter.format(record));
}
}
};
}
return logHandler;
}
public void notifyExceptionOccured(GenerationException exception) {
if (handleLogs) {
StackTraceElement[] st = exception.getStackTrace();
err("Exception occured: " + exception.getLocalizedMessage() + "\n");
err("Details:\n" + exception.getDetails() + "\n");
err(exception.toString() + "\n");
for (StackTraceElement el : st) {
err("\tat " + el + "\n");
}
if (exception.getTargetException() != null) {
err("Caused by:\n");
err(exception.getTargetException().toString() + "\n");
for (StackTraceElement el : exception.getTargetException().getStackTrace()) {
err("\tat " + el + "\n");
}
Throwable t = exception.getTargetException();
while (t.getCause() != null) {
t = t.getCause();
}
if (!exception.getTargetException().equals(t)) {
err("Deeply caused by:\n");
err(t.toString() + "\n");
for (StackTraceElement el : t.getStackTrace()) {
err("\tat " + el + "\n");
}
}
}
getLogHandler().flush();
}
}
public void startHandleLogs() {
handleLogs = true;
for (Logger tempLogger : loggers) {
if (tempLogger != null) {
boolean alreadyInside = false;
for (Handler already : tempLogger.getHandlers()) {
if (already == getLogHandler()) {
alreadyInside = true;
}
}
if (!alreadyInside) {
tempLogger.addHandler(getLogHandler());
}
}
}
}
public void flushLogs() {
getLogHandler().flush();
}
public void stopHandleLogs() {
handleLogs = false;
for (Logger tempLogger : loggers) {
if (tempLogger != null) {
tempLogger.removeHandler(getLogHandler());
}
}
}
public void addToLogListeners(LogListener listener) {
logListeners.add(listener);
}
public void removeFromLogListeners(LogListener listener) {
logListeners.remove(listener);
}
public void copyAdditionalFiles() throws IOException {
// To overwrite if the repository needs additional files
}
public abstract boolean hasBeenInitialized();
/**
* Override this method to force an order for the file generation. <br>
* Useful is a generation can alter the result of another generation.
*
* @param resources
* the resources which will be generated
*/
public void sortResourcesForGeneration(
List<CGRepositoryFileResource<? extends GeneratedResourceData, IFlexoResourceGenerator, CGFile>> resources) {
FlexoResource.sortResourcesWithDependancies(resources);
}
}