/* * (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; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.logging.Level; import java.util.logging.Logger; import org.openflexo.foundation.bindings.Bindable; import org.openflexo.foundation.rm.FlexoResourceData; import org.openflexo.foundation.rm.FlexoXMLStorageResource; import org.openflexo.foundation.rm.RMNotification; import org.openflexo.foundation.rm.XMLStorageResourceData; import org.openflexo.foundation.xml.FlexoBuilder; import org.openflexo.foundation.xml.FlexoXMLSerializable; import org.openflexo.localization.FlexoLocalization; import org.openflexo.xmlcode.Cloner; import org.openflexo.xmlcode.StringEncoder; import org.openflexo.xmlcode.XMLCoder; import org.openflexo.xmlcode.XMLDecoder; import org.openflexo.xmlcode.XMLMapping; /** * This abstract class represents an object, or "data" in the model-view paradigm. That can be serialized/deserialized through XMLCoDe * scheme * * @author sguerin * */ public abstract class FlexoXMLSerializableObject extends FlexoObservable implements FlexoXMLSerializable { private static final Logger logger = Logger.getLogger(FlexoXMLSerializableObject.class.getPackage().getName()); protected boolean isDeserializing = false; protected boolean isSerializing = false; private boolean isModified; private Date lastMemoryUpdate; private Thread serializingThread; @Override public abstract XMLMapping getXMLMapping(); @Override public Object instanciateNewBuilder() { return getXMLResourceData().getFlexoXMLFileResource().instanciateNewBuilder(); } /* * public FlexoXMLSerializableObject cloneUsingXMLMapping() { String * xmlRepresentation = getXMLRepresentation(); if * (getXMLMapping().hasBuilderClass()) { Object builder = * instanciateNewBuilder(); if (builder == null) { if * (logger.isLoggable(Level.WARNING)) logger.warning ("Model for encoding * "+getClass().getName()+" defines a builder while builder instanciation * returns null"); } if * (getXMLMapping().builderClass().isAssignableFrom(builder.getClass())) { * return instanciateFromXML * (xmlRepresentation,getXMLMapping(),instanciateNewBuilder()); } else { if * (logger.isLoggable(Level.WARNING)) logger.warning ("Model for encoding * "+getClass().getName() +" defines a builder of class " * +getXMLMapping().builderClass().getName() +" while builder instanciation * returns an object of class " +builder.getClass().getName()); } return * null; } else { return * instanciateFromXML(xmlRepresentation,getXMLMapping()); } } */ @Override public FlexoXMLSerializable cloneUsingXMLMapping() { return cloneUsingXMLMapping(null, true, getXMLMapping()); } public FlexoXMLSerializable cloneUsingXMLMapping(boolean setIsBeingCloned) { return cloneUsingXMLMapping(null, setIsBeingCloned, getXMLMapping()); } public FlexoXMLSerializable cloneUsingXMLMapping(Object builder, boolean setIsBeingCloned, XMLMapping xmlMapping) { if (setIsBeingCloned) { if (getXMLResourceData() != null) { getXMLResourceData().initializeCloning(); } else { isBeingCloned = true; } } if (xmlMapping.hasBuilderClass()) { if (builder == null) { builder = instanciateNewBuilder(); } if (builder instanceof FlexoBuilder) { ((FlexoBuilder<FlexoXMLStorageResource>) builder).isCloner = true; } if (xmlMapping.builderClass().isAssignableFrom(builder.getClass())) { try { FlexoXMLSerializableObject obj = (FlexoXMLSerializableObject) Cloner.cloneObjectWithMapping(this, xmlMapping, builder, getStringEncoder()); return obj; } catch (Exception e) { // Warns about the exception if (logger.isLoggable(Level.WARNING)) { logger.warning("Exception raised: " + e.getClass().getName() + ". See console for details."); } e.printStackTrace(); return null; } finally { if (getXMLResourceData() != null) { getXMLResourceData().finalizeCloning(); } } } else { if (logger.isLoggable(Level.WARNING)) { logger.warning("Model for encoding " + getClass().getName() + " defines a builder of class " + xmlMapping.builderClass().getName() + " while builder instanciation returns an object of class " + builder.getClass().getName()); } } if (getXMLResourceData() != null) { getXMLResourceData().finalizeCloning(); } isBeingCloned = false; return null; } else { try { FlexoXMLSerializableObject obj = (FlexoXMLSerializableObject) Cloner.cloneObjectWithMapping(this, xmlMapping, StringEncoder.getDefaultInstance(), getStringEncoder()); return obj; } catch (Exception e) { // Warns about the exception if (logger.isLoggable(Level.WARNING)) { logger.warning("Exception raised: " + e.getClass().getName() + ". See console for details."); } e.printStackTrace(); return null; } finally { if (getXMLResourceData() != null) { getXMLResourceData().finalizeCloning(); } isBeingCloned = false; } } } public String getXMLRepresentation() { try { initializeSerialization(); String returned = XMLCoder.encodeObjectWithMapping(this, getXMLMapping(), getStringEncoder(), null); finalizeSerialization(); return returned; } catch (Exception e) { // Warns about the exception if (logger.isLoggable(Level.WARNING)) { logger.warning("Exception raised: " + e.getClass().getName() + ". See console for details."); } e.printStackTrace(); return null; } } public void saveToFile(File aFile) { FileOutputStream out = null; try { if (!aFile.getParentFile().exists()) { aFile.getParentFile().mkdirs(); } out = new FileOutputStream(aFile); XMLCoder.encodeObjectWithMapping(this, getXMLMapping(), out, getStringEncoder()); out.flush(); } catch (Exception e) { // Warns about the exception if (logger.isLoggable(Level.WARNING)) { logger.warning("Exception raised: " + e.getClass().getName() + ". See console for details."); } e.printStackTrace(); } finally { if (out != null) { try { out.close(); } catch (IOException e) { e.printStackTrace(); } } } } public static FlexoXMLSerializableObject instanciateFromXML(String anXMLRepresentation, XMLMapping mapping, Object builder, StringEncoder stringEncoder) { try { return (FlexoXMLSerializableObject) XMLDecoder.decodeObjectWithMapping(anXMLRepresentation, mapping, builder, stringEncoder); } catch (Exception e) { // Warns about the exception if (logger.isLoggable(Level.WARNING)) { logger.warning("Exception raised: " + e.getClass().getName() + ". See console for details."); } e.printStackTrace(); return null; } } /* * public static FlexoXMLSerializableObject instanciateFromXML (String * anXMLRepresentation, XMLMapping mapping) { try { return * (FlexoXMLSerializableObject)XMLDecoder.decodeObjectWithMapping(anXMLRepresentation,mapping); } * catch (Exception e) { // Warns about the exception if * (logger.isLoggable(Level.WARNING)) logger.warning ("Exception raised: * "+e.getClass().getName()+". See console for details."); * e.printStackTrace(); return null; } } */ public void initializeDeserialization(Object builder) { if (logger.isLoggable(Level.FINER)) { logger.finer("initializeDeserialization() for " + this.getClass().getName()); } isDeserializing = true; _builder = builder; if (builder instanceof FlexoBuilder) { if (((FlexoBuilder<FlexoXMLStorageResource>) builder).isCloner) { isCreatedByCloning = true; } } // Modified by DVA on June 2006: getBindingModel() cannot be called now. too soon !! try { if (this instanceof Bindable && ((Bindable) this).getBindingModel() != null) { if (builder instanceof FlexoBuilder) { ((FlexoBuilder) builder).getProject().getAbstractBindingConverter().setBindable((Bindable) this); ((FlexoBuilder) builder).getProject().getBindingAssignementConverter().setBindable((Bindable) this); // ((FlexoBuilder) builder).getProject().getBindingValueConverter().setBindable((Bindable) this); // ((FlexoBuilder) builder).getProject().getBindingExpressionConverter().setBindable((Bindable) this); } else if (logger.isLoggable(Level.SEVERE)) { logger.severe("Builder is not a FlexoBuilder!!!"); } } } catch (RuntimeException e) { e.printStackTrace(); } } private Object _builder; protected Object getBuilder() { return _builder; } /** * Property that indicates wheter the current object is currently being created from another already existing object */ private boolean isCreatedByCloning = false; /** * Property that indicates wheter the current object is currently being created from another already existing object */ public boolean isCreatedByCloning() { return isCreatedByCloning; } /** * Property that indicates wheter the current object is currently copied to create a new clone */ private boolean isBeingCloned = false; public void initializeCloning() { isBeingCloned = true; } public void finalizeCloning() { isBeingCloned = false; } /** * Property that indicates wheter the current object is currently copied to create a new clone */ public boolean isBeingCloned() { if (getXMLResourceData() == this) { return isBeingCloned; } else { if (getXMLResourceData() != null) { return getXMLResourceData().isBeingCloned(); } else { return isBeingCloned; } } } public void finalizeDeserialization(Object builder) { if (logger.isLoggable(Level.FINER)) { logger.finer("finalizeDeserialization() for " + this.getClass().getName()); } if (builder instanceof FlexoBuilder) { // GPO: I really think that this line has absolutely no effect but since it was there before, I leave it ((FlexoBuilder<FlexoXMLStorageResource>) builder).isCloner = false; } // Note: we need to clear is modified before setting isDeserializing to false // See method clearIsModified in FlexoProject. clearIsModified(true);// we just finished deserializing so all update dates are wrong and should be forgotten isDeserializing = false; isCreatedByCloning = false; _builder = null; } public final void finalizeDeserialization() { finalizeDeserialization(null); } public Thread getSerializingThread() { return serializingThread; } public void setSerializingThread(Thread serializingThread) { this.serializingThread = serializingThread; } public void initializeSerialization() { if (logger.isLoggable(Level.FINER)) { logger.finer("initializeSerialization() for " + this.getClass().getName()); } if (serializingThread == null || serializingThread == Thread.currentThread()) { serializingThread = Thread.currentThread(); } else if (logger.isLoggable(Level.SEVERE)) { logger.severe("Two different threads are trying to serialize " + getXMLResourceData() + "\n Thread already serializing is:" + serializingThread + " and the new one is " + Thread.currentThread()); } isSerializing = true; } public void finalizeSerialization() { if (logger.isLoggable(Level.FINER)) { logger.finer("finalizeSerialization() for " + this.getClass().getName()); } isSerializing = false; serializingThread = null; } public boolean isSerializing() { if (getXMLResourceData() == this) { return isSerializing && serializingThread == Thread.currentThread(); } else { return getXMLResourceData() != null && getXMLResourceData().isSerializing(); } } public boolean isDeserializing() { if (getXMLResourceData() == this) { return isDeserializing; } else { if (getXMLResourceData() != null) { return getXMLResourceData().isDeserializing(); } else { return isDeserializing; } } } public boolean isResourceModified() { if (getXMLResourceData() != null) { return getXMLResourceData().isModified(); } else { return isModified(); } } public Date resourceLastMemoryUpdate() { if (getXMLResourceData() != null) { return getXMLResourceData().lastMemoryUpdate(); } else { return lastMemoryUpdate(); } } public Date resourceLastUpdate() { if (getXMLResourceData() != null && getXMLResourceData().getFlexoResource() != null) { return getXMLResourceData().getFlexoResource().getLastUpdate(); } return null; } public boolean isModified() { return isModified; } public Date lastMemoryUpdate() { if (lastMemoryUpdate == null) { if (getXMLResourceData() != null && getXMLResourceData() != this) { lastMemoryUpdate = getXMLResourceData().lastMemoryUpdate(); } } return lastMemoryUpdate; } private boolean ignoreNotifications = false; public boolean ignoreNotifications() { if (getXMLResourceData() == null || getXMLResourceData() == this) { return ignoreNotifications; } else if (getXMLResourceData() instanceof FlexoXMLSerializableObject) { return ((FlexoXMLSerializableObject) getXMLResourceData()).ignoreNotifications(); } return false; } /** * Temporary method to prevent notifications. This should never be used by anyone except the screenshot generator */ public final void setIgnoreNotifications() { ignoreNotifications = true; } /** * Temporary method to reactivate notifications. This should never be used by anyone except the screenshot generator */ public final void resetIgnoreNotifications() { ignoreNotifications = false; } public synchronized void setIsModified() { if (ignoreNotifications) { return; } if (getXMLResourceData() == this && isModified == false && getXMLResourceData().getFlexoResource() != null) { // (new Exception("Resource "+getXMLResourceData().getFlexoResource()+" has been modified")).printStackTrace(); logger.info(">>>>>>> Resource " + getXMLResourceData().getFlexoResource() + " has been modified"); // A resource has been modified, while it's wasn't before // Don't forget to notify resource isModified = true; lastMemoryUpdate = new Date(); getXMLResourceData().getFlexoResource().notifyResourceStatusChanged(); } isModified = true; lastMemoryUpdate = new Date(); } /** * This date is use to perform fine tuning resource dependancies computing * * @return */ public Date getLastUpdate() { if (lastMemoryUpdate == null) { return resourceLastUpdate(); } return lastMemoryUpdate; } public String getLastUpdateAsString() { if (getLastUpdate() != null) { if (getLastUpdate().equals(new Date(0))) { return FlexoLocalization.localizedForKey("never"); } return new SimpleDateFormat("dd/MM HH:mm:ss SSS").format(getLastUpdate()); } return "???"; } public synchronized void clearIsModified(boolean clearLastMemoryUpdate) { isModified = false; // GPO: I commented the line hereunder because I don't think that we need to reset this date if (clearLastMemoryUpdate) { lastMemoryUpdate = null; } } @Override public void setChanged() { setChanged(true); } public final void setChanged(boolean propagateModified) { /* * The final keyword is added here mainly because this part of the code * is highly sensitive. A synchronized modifier could cause many * problems (essentially with the auto-saving thread) */ // logger.info("called setChanged in "+getClass().getName()+" isDeserializing()="+isDeserializing()); if (isSerializing()) { return; } synchronized (this) { super.setChanged(); if (!isDeserializing() && !isSerializing()) { if (propagateModified) { setIsModified(); } if (getXMLResourceData() != null && getXMLResourceData() != this) { // This object is embedded in an XMLResourceData if (propagateModified) { getXMLResourceData().setIsModified(); } } } } } /** * Returns reference to the main object in which this XML-serializable object is contained relating to storing scheme * * @return */ @Override public abstract XMLStorageResourceData getXMLResourceData(); /** * * Implements * * @see org.openflexo.foundation.rm.FlexoResourceData#notifyRM(org.openflexo.foundation.rm.RMNotification) by forwarding * @see org.openflexo.foundation.rm.RMNotification to the related resource, if and only if this object represents the resource data * itself (@see org.openflexo.foundation.rm.FlexoResourceData). Otherwise, invoking this method is ignored. * * @see org.openflexo.foundation.rm.FlexoResourceData#notifyRM(org.openflexo.foundation.rm.RMNotification) */ public void notifyRM(RMNotification notification) throws FlexoException { if (this instanceof FlexoResourceData && ((FlexoResourceData) this).getFlexoResource() != null) { ((FlexoResourceData) this).getFlexoResource().notifyRM(notification); } } /** * Implements * * @see org.openflexo.foundation.rm.FlexoResourceData#receiveRMNotification(org.openflexo.foundation.rm.RMNotification) Receive a * notification that has been propagated by the ResourceManager scheme and coming from a modification on an other resource This * method is relevant if and only if this object represents the resource data itself (@see * org.openflexo.foundation.rm.FlexoResourceData). At this level, this method is ignored and just return, so you need to override * it in subclasses if you want to get the hook to do your stuff ! * * @see org.openflexo.foundation.rm.FlexoResourceData#receiveRMNotification(org.openflexo.foundation.rm.RMNotification) */ public void receiveRMNotification(RMNotification notification) throws FlexoException { // Ignore it at this level: please overrides this method in relevant // subclasses ! } /** * Overrides * * @see org.openflexo.foundation.FlexoObservable#notifyObservers(org.openflexo.foundation.DataModification) by propagating * dataModification if this one implements * @see org.openflexo.foundation.rm.RMNotification to the related resource * * @see org.openflexo.foundation.FlexoObservable#notifyObservers(org.openflexo.foundation.DataModification) */ @Override public void notifyObservers(DataModification dataModification) { if (isSerializing()) { return; } super.notifyObservers(dataModification); if (dataModification instanceof RMNotification && getXMLResourceData() != null) { try { getXMLResourceData().notifyRM((RMNotification) dataModification); } catch (FlexoException e) { // Warns about the exception if (logger.isLoggable(Level.WARNING)) { logger.warning("FLEXO Exception raised: " + e.getClass().getName() + ". See console for details."); } e.printStackTrace(); } } } }