/* * (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.rm; import java.awt.Rectangle; import java.awt.image.BufferedImage; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.util.Date; import java.util.Vector; import java.util.logging.Level; import java.util.logging.Logger; import org.openflexo.foundation.DataModification; import org.openflexo.foundation.FlexoException; import org.openflexo.foundation.FlexoModelObject; import org.openflexo.foundation.FlexoObservable; import org.openflexo.foundation.FlexoObserver; import org.openflexo.foundation.NameChanged; import org.openflexo.foundation.ObjectDeleted; import org.openflexo.foundation.dm.ERDiagram; import org.openflexo.foundation.gen.ScreenshotGenerator; import org.openflexo.foundation.gen.ScreenshotGenerator.ScreenshotImage; import org.openflexo.foundation.ie.IEWOComponent; import org.openflexo.foundation.ie.cl.ComponentDefinition; import org.openflexo.foundation.ie.cl.TabComponentDefinition; import org.openflexo.foundation.ie.dm.ComponentNameChanged; import org.openflexo.foundation.utils.FlexoModelObjectReference; import org.openflexo.foundation.utils.FlexoProjectFile; import org.openflexo.foundation.view.View; import org.openflexo.foundation.view.ViewDefinition; import org.openflexo.foundation.wkf.FlexoProcess; import org.openflexo.foundation.wkf.FlexoWorkflow; import org.openflexo.foundation.wkf.RoleList; import org.openflexo.foundation.wkf.node.AbstractActivityNode; import org.openflexo.foundation.wkf.node.LOOPOperator; import org.openflexo.foundation.wkf.node.OperationNode; import org.openflexo.logging.FlexoLogger; import org.openflexo.swing.ImageUtils; import org.openflexo.swing.ImageUtils.ImageType; import org.openflexo.toolbox.FileUtils; import org.openflexo.toolbox.ImageInfo; /** * @author sylvain * */ public class ScreenshotResource extends FlexoGeneratedResource<ScreenshotResource.ScreenshotData> implements FlexoObserver, FlexoModelObjectReference.ReferenceOwner { protected static final Logger logger = FlexoLogger.getLogger(ScreenshotResource.class.getPackage().getName()); public static final ImageType SCREENSHOT_TYPE = ImageType.PNG; public static final String DOTTED_SCREENSHOT_EXTENSION = "." + SCREENSHOT_TYPE.getExtension(); // private FlexoModelObject source; private String sourceReferenceString; private FlexoModelObjectReference<FlexoModelObject> sourceReference; private boolean willBeDeleted = false; private String name; protected Rectangle trimInfo; @Override public String getName() { return name; } /** * */ private void checkResourceNameIsUpToDate() { if (getModelObject() != null) { String s = ScreenshotGenerator.getScreenshotName(getModelObject()); if (name != null && !name.equals(s) && project != null && !project.isDeserializing() && !project.isSerializing()) { if (logger.isLoggable(Level.INFO)) { logger.info("Renaming resource " + name + " to " + s); } setName(s); } } } @Override public void setName(String aName) { if (aName == null) { return; } if (name != null && name.equals(aName)) { return; } String oldName = name; name = aName; try { if (resourceFile != null && !getFile().getName().equals(aName + DOTTED_SCREENSHOT_EXTENSION) && !getFile().getName().equals(aName)) { if (!getFile().exists()) { try { getFile().createNewFile(); } catch (IOException e) { e.printStackTrace(); } } boolean b = renameFileTo(name + DOTTED_SCREENSHOT_EXTENSION); if (!b && logger.isLoggable(Level.WARNING)) { logger.warning("Could not rename screenshot resource from" + getFile().getName() + " to " + name); } if (b) { getProject().renameResource(this, name); } } } catch (DuplicateResourceException e) { e.printStackTrace(); name = oldName; delete(); } catch (InvalidFileNameException e) { if (logger.isLoggable(Level.WARNING)) { logger.warning("InvalidFileName occured while trying to rename screenshot with name " + name + ". Deleting it."); } e.printStackTrace(); name = oldName; delete(); } catch (Exception e) { if (logger.isLoggable(Level.WARNING)) { logger.warning("An exception occured while trying to rename screenshot with name " + name + ". Deleting it."); } e.printStackTrace(); name = oldName; delete(); } } public static ScreenshotResource createNewScreenshotForObject(FlexoModelObject o) { /** * Little help if you want to create a screenshot for an object: * * org.openflexo.foundation.rm.ScreenshotResource.createNewScreenshotForObject(FlexoModelObject) * * org.openflexo.foundation.gen.ScreenshotGenerator.getScreenshotName(Object) * * org.openflexo.foundation.gen.ScreenshotGenerator.getImage(FlexoModelObject) (twice) * * org.openflexo.dg.latex.ScreenshotsGenerator.getScreenshot(FlexoModelObject) * * org.openflexo.dg.latex.ScreenshotsGenerator.buildResourcesAndSetGenerators(DGRepository, Vector<CGRepositoryFileResource>) * and * in the method of the module that will generate the screenshot Clearly this could be improved but I don't have time for it. */ ScreenshotResource ret = new ScreenshotResource(o.getProject()); String name = ScreenshotGenerator.getScreenshotName(o); ret.setName(name); if (o instanceof FlexoProcess) { ret.setSource(o); } else if (o instanceof AbstractActivityNode) { ret.setSource(o); } else if (o instanceof IEWOComponent) { IEWOComponent comp = (IEWOComponent) o; ret.setSource(comp.getComponentDefinition()); // We use the component definition as the source but we depend of the // WOComponentResource (@see #rebuildDependancies()) } else if (o instanceof ComponentDefinition) { ComponentDefinition comp = (ComponentDefinition) o; ret.setSource(comp); // We use the component definition as the source but we depend of the WOComponentResource (@see // #rebuildDependancies()) } else if (o instanceof OperationNode) { OperationNode node = (OperationNode) o; ret.setSource(node); } else if (o instanceof LOOPOperator) { ret.setSource(o); } else if (o instanceof ERDiagram) { ret.setSource(o); } else if (o instanceof RoleList) { ret.setSource(o); } else if (o instanceof FlexoWorkflow) { ret.setSource(o); } else if (o instanceof ViewDefinition) { ret.setSource(o); } else if (o instanceof View) { ret.setSource(((View) o).getShemaDefinition()); } else { logger.warning("Could not create screenshot for " + o); return null; } ret.rebuildDependancies(); FlexoProjectFile file = new FlexoProjectFile(ProjectRestructuration.GENERATED_DOC_DIR + "/" + name + DOTTED_SCREENSHOT_EXTENSION); try { ret.setResourceFile(file); } catch (InvalidFileNameException e1) { file = new FlexoProjectFile(FileUtils.getValidFileName(file.getRelativePath())); try { ret.setResourceFile(file); } catch (InvalidFileNameException e) { if (logger.isLoggable(Level.SEVERE)) { logger.severe("Invalid file name: " + file.getRelativePath() + ". This should never happen."); } return null; } } ret.setName(name); try { o.getProject().registerResource(ret); } catch (DuplicateResourceException e) { // Warns about the exception logger.warning("Exception raised: " + e.getClass().getName() + ". See console for details."); e.printStackTrace(); } return ret; } /** * @param builder */ public ScreenshotResource(FlexoProjectBuilder builder) { super(builder); builder.notifyResourceLoading(this); } /** * @param aProject */ public ScreenshotResource(FlexoProject aProject) { super(aProject); } /** * Overrides generateResourceData * * @see org.openflexo.foundation.rm.FlexoGeneratedResource#createResourceData() */ /* * protected FlexoResourceData createResourceData() { FlexoResourceData data = * new ScreenshotData(getFile()); data.setProject(getProject()); try { * data.setFlexoResource(this); } catch (DuplicateResourceException e) { } * resourceData = data; return data; } */ /** * Overrides getResourceType * * @see org.openflexo.foundation.rm.FlexoResource#getResourceType() */ @Override public ResourceType getResourceType() { return ResourceType.SCREENSHOT; } /** * Overrides isDeleted * * @see org.openflexo.foundation.rm.FlexoResource#isDeleted() */ @Override public boolean isDeleted() { return super.isDeleted(); } @Override public boolean optimisticallyDependsOf(FlexoResource resource, Date requestDate) { if (resource instanceof FlexoProcessResource) { if (!requestDate.before(((FlexoProcessResource) resource).getFlexoProcess().getLastUpdate())) { return false; } } else if (resource instanceof FlexoComponentResource) { if (!checkOptimisticDependancyForComponent(resource, requestDate)) { return false; } } return super.optimisticallyDependsOf(resource, requestDate); } /** * @param resource * @param requestDate */ private boolean checkOptimisticDependancyForComponent(FlexoResource resource, Date requestDate) { if (lastUpdateDateIsBefore(((FlexoComponentResource) resource).getResourceData(), requestDate)) { boolean depends = false; Vector<TabComponentDefinition> tabs = new Vector<TabComponentDefinition>(); ((FlexoComponentResource) resource).getResourceData().getAllTabComponents(tabs); for (TabComponentDefinition tab : tabs) { if (tab.isLoaded()) { depends |= !lastUpdateDateIsBefore(tab.getWOComponent(), requestDate); } } return depends; } return true; } private boolean lastUpdateDateIsBefore(IEWOComponent component, Date requestDate) { return component.getLastUpdate() != null && !requestDate.before(component.getLastUpdate()); } @Override public boolean checkIntegrity() { return getSourceReference() != null; } public FlexoModelObject getModelObject() { if (getSourceReference() != null) { return sourceReference.getObject(true); } else { if (!willBeDeleted) { willBeDeleted = true; this.delete(false); } } return null; } @Override public boolean isGeneratedResourceDataReadable() { return true; } @Override public ScreenshotData readGeneratedResourceData() { return createGeneratedResourceData(); } @Override protected ScreenshotData createGeneratedResourceData() { return new ScreenshotData(); } public ScreenshotData getScreenshotData() { return getGeneratedResourceData(); } /** * Overrides setResourceFile * * @see org.openflexo.foundation.rm.FlexoFileResource#setResourceFile(org.openflexo.foundation.utils.FlexoProjectFile) */ @Override public void setResourceFile(FlexoProjectFile aFile) throws InvalidFileNameException { if (aFile.getRelativePath() != null && !aFile.getRelativePath().toLowerCase().endsWith(DOTTED_SCREENSHOT_EXTENSION)) { String relPath = ""; if (aFile.getRelativePath().indexOf('/') > -1) { relPath = aFile.getRelativePath().substring(0, aFile.getRelativePath().lastIndexOf('/') + 1); } if (name != null) { aFile.setRelativePath(relPath + name + DOTTED_SCREENSHOT_EXTENSION); } } super.setResourceFile(aFile); } /** * Overrides finalizeGeneration * * @see org.openflexo.foundation.rm.FlexoGeneratedResource#finalizeGeneration() */ @Override public void finalizeGeneration() { super.finalizeGeneration(); resetGeneratedResourceData(); } public class ScreenshotData implements GeneratedResourceData { protected ScreenshotImage data; public BufferedImage getImage() { if (data != null) { return data.image; } return null; } /** * */ public ScreenshotData() { } /** * Overrides getFlexoResource * * @see org.openflexo.foundation.rm.FlexoResourceData#getFlexoResource() */ @Override public ScreenshotResource getFlexoResource() { return ScreenshotResource.this; } public ScreenshotResource getScreenshotResource() { return getFlexoResource(); } /** * Overrides setFlexoResource * * @see org.openflexo.foundation.rm.FlexoResourceData#setFlexoResource(org.openflexo.foundation.rm.FlexoResource) */ @Override public void setFlexoResource(FlexoResource resource) throws DuplicateResourceException { } /** * Overrides getProject * * @see org.openflexo.foundation.rm.FlexoResourceData#getProject() */ @Override public FlexoProject getProject() { return getFlexoResource().getProject(); } @Override public void generate() { generateScreenshot(); } @Override public void regenerate() { generateScreenshot(); } private void generateScreenshot() { data = ScreenshotGenerator.getImage(getModelObject()); if (data != null) { trimInfo = data.trimInfo; } } /** * @throws FlexoException * */ @Override public void writeToFile(File aFile) throws FlexoException { if (data == null) { if (logger.isLoggable(Level.SEVERE)) { logger.severe("Called write to file without having called generate on screenshot resource data: " + getFlexoResource()); } return; } /* * if (needsGeneration) generateScreenshot(); */ File image = aFile; File path = image.getParentFile(); if (!path.exists()) { path.mkdirs(); } if (logger.isLoggable(Level.INFO)) { logger.info("Writing file " + image.getAbsolutePath()); } try { ImageUtils.saveImageToFile(data.image, image, SCREENSHOT_TYPE); } catch (Exception e) { throw new FlexoException("Error while writing image to " + image.getAbsolutePath(), e); } } } /** * Overrides generateData * * @see org.openflexo.foundation.rm.FlexoGeneratedResource#generateData() */ /* * protected void generateData() throws GenerateResourceException { * getScreenshotData().generateScreenshot(); } */ /** * Overrides update * * @see org.openflexo.foundation.FlexoObserver#update(org.openflexo.foundation.FlexoObservable, * org.openflexo.foundation.DataModification) */ @Override public void update(FlexoObservable observable, DataModification dataModification) { if (dataModification instanceof NameChanged) { if (getModelObject() == observable || getModelObject() instanceof AbstractActivityNode && observable == ((AbstractActivityNode) getModelObject()).getProcess() || getModelObject() instanceof OperationNode && observable == ((OperationNode) getModelObject()).getProcess() || getModelObject() instanceof OperationNode && observable == ((OperationNode) getModelObject()).getAbstractActivityNode()) { checkResourceNameIsUpToDate(); if (logger.isLoggable(Level.FINEST)) { logger.finest("Renamed screenshot due to a rename in the workflow"); } } } else if (dataModification instanceof ComponentNameChanged && observable == getModelObject()) { checkResourceNameIsUpToDate(); if (logger.isLoggable(Level.FINEST)) { logger.finest("Renamed screenshot due to component rename."); } } else if (dataModification instanceof ObjectDeleted && ((ObjectDeleted) dataModification).getDeletedObject() == getModelObject()) { delete(false); } } private boolean isObserving = false; public void startObserving() { if (isObserving) { return; } if (getModelObject() != null) { getModelObject().addObserver(this); if (getModelObject() instanceof AbstractActivityNode) { ((AbstractActivityNode) getModelObject()).getProcess().addObserver(this); } else if (getModelObject() instanceof OperationNode) { ((OperationNode) getModelObject()).getProcess().addObserver(this); ((OperationNode) getModelObject()).getAbstractActivityNode().addObserver(this); } isObserving = true; } } private void stopObserving() { if (getModelObject() != null) { getModelObject().deleteObserver(this); } if (getModelObject() instanceof AbstractActivityNode && ((AbstractActivityNode) getModelObject()).getProcess() != null) { ((AbstractActivityNode) getModelObject()).getProcess().deleteObserver(this); } isObserving = false; } /** * Delete this resource. Delete file is flag deleteFile is true. */ @Override public synchronized void delete(boolean deleteFile) { willBeDeleted = true; stopObserving(); if (sourceReference != null) { sourceReference.delete(false); } super.delete(deleteFile); } /** * Overrides rebuildDependancies * * @see org.openflexo.foundation.rm.FlexoResource#rebuildDependancies() */ @Override public void rebuildDependancies() { super.rebuildDependancies(); if (getModelObject() != null && getModelObject().getXMLResourceData() != null && getModelObject().getXMLResourceData().getFlexoResource() != null) { if (!(getModelObject() instanceof ComponentDefinition) && !(getModelObject() instanceof ViewDefinition)) { addToDependentResources(getModelObject().getXMLResourceData().getFlexoResource()); } } if (getModelObject() instanceof ComponentDefinition) { // On the next line we pass true because the WDLDateAssistantPopup is virtually created but does not // create its res resource automatically. FlexoComponentResource compRes = ((ComponentDefinition) getModelObject()).getComponentResource(true); if (compRes != null) { addToDependentResources(compRes); } } if (getModelObject() instanceof ViewDefinition) { FlexoOEShemaResource viewRes = ((ViewDefinition) getModelObject()).getShemaResource(false); if (viewRes != null) { addToDependentResources(viewRes); } } } public Rectangle getTrimInfo() { if (trimInfo == null) { ImageInfo ii = new ImageInfo(); FileInputStream fis = null; if (getFile() != null && getFile().exists()) { try { ii.setInput(fis = new FileInputStream(getFile())); ii.check(); trimInfo = new Rectangle(0, 0, ii.getWidth(), ii.getHeight()); ii = null; } catch (FileNotFoundException e) { e.printStackTrace(); } finally { if (fis != null) { try { fis.close(); } catch (IOException e) { e.printStackTrace(); } } } } } return trimInfo; } public void setTrimInfo(Rectangle trimInfo) { this.trimInfo = trimInfo; } public FlexoModelObjectReference<FlexoModelObject> getSourceReference() { if (sourceReference == null && sourceReferenceString != null) { if (getProject() != null) { setSourceReference(getProject().getObjectReferenceConverter().convertFromString(sourceReferenceString)); sourceReferenceString = null; } else { if (logger.isLoggable(Level.SEVERE)) { logger.severe("No project on screenshot: " + getName()); } } } return sourceReference; } private void setSourceReference(FlexoModelObjectReference<FlexoModelObject> sourceReference) { if (sourceReference == null) { if (logger.isLoggable(Level.SEVERE)) { logger.severe("Trying to set a null source reference, this is not allowed!"); } } this.sourceReference = sourceReference; if (sourceReference != null) { sourceReference.setSerializeClassName(true); sourceReference.setOwner(this); } } public String getSourceReferenceString() { if (sourceReference != null) { if (getProject() != null) { return getProject().getObjectReferenceConverter().convertToString(sourceReference); } else { if (logger.isLoggable(Level.SEVERE)) { logger.severe("No project on screenshot: " + getName()); } } } return sourceReferenceString; } public void setSourceReferenceString(String sourceReferenceString) { this.sourceReferenceString = sourceReferenceString; } private void setSource(FlexoModelObject o) { setSourceReference(new FlexoModelObjectReference<FlexoModelObject>(o, this)); } @Override public void notifyObjectLoaded(FlexoModelObjectReference reference) { checkResourceNameIsUpToDate(); startObserving(); } @Override public void objectCantBeFound(FlexoModelObjectReference reference) { if (logger.isLoggable(Level.WARNING)) { logger.warning("Could not find object for reference: " + reference + ", deleting resource " + this); } if (!willBeDeleted) { willBeDeleted = true; this.delete(false); } } @Override public void objectDeleted(FlexoModelObjectReference reference) { if (!willBeDeleted) { willBeDeleted = true; this.delete(false); } } @Override public void objectSerializationIdChanged(FlexoModelObjectReference reference) { setChanged(); } }