Class ALcyWorkspaceCodec

java.lang.Object
com.luciad.lucy.workspace.ALcyWorkspaceCodec
All Implemented Interfaces:
ILcdPropertyChangeSource
Direct Known Subclasses:
TLcyBlobWorkspaceCodec

public abstract class ALcyWorkspaceCodec extends Object implements ILcdPropertyChangeSource
The ALcyWorkspaceCodec is responsible for encoding/decoding a workspace. Encoding a workspace can be done using TLcyWorkspaceManager.encodeWorkspace(String) and TLcyWorkspaceManager.decodeWorkspace(String) which will delegate to the methods encodeWorkspace(String) and decodeWorkspace(String) of this ALcyWorkspaceCodec.

This class is responsible to lead the encoding/decoding process and to delegate to the registered ALcyWorkspaceCodecDelegates and ALcyWorkspaceObjectCodecs. It also deals with (circular) references and avoids that objects are encoded twice.

A workspace is basically encoded by asking all registered ALcyWorkspaceCodecDelegates to encode themselves. Whenever one of these needs to encode an object reference (e.g. a reference to an ILcdGXYLayer), they will use encodeReference(Object) to do so. This will in turn trigger this ALcyWorkspaceCodec to find a registered ALcyWorkspaceObjectCodec that can encode the object. Encoding such an object might trigger the encoding of other objects, e.g. encoding an ILcdGXYLayer l will probably trigger the encoding of a reference to l.getModel().

A workspace is decoded by decoding all encoded parts by the correct ALcyWorkspaceCodecDelegate. For every part, the ALcyWorkspaceCodecDelegate.getUID() is used to find the correct ALcyWorkspaceCodecDelegate, the one that encoded that part. Decoding such a part might involve the need to restore an object reference (e.g. a reference to an ILcdGXYLayer). The ALcyWorkspaceCodecDelegate will use decodeReference(String) to do so. This will in turn trigger this ALcyWorkspaceCodec to find the correct ALcyWorkspaceObjectCodec to decode the object. The ALcyWorkspaceObjectCodec.getUID() is used for this purpose.

Whenever file/directory names need to be encoded/decoded by any ALcyWorkspaceCodecDelegate or ALcyWorkspaceObjectCodec, it is advised to use encodePath(String) and decodePath(String). This allows subclasses for example to store paths relative to the location of the workspace file.

Each ALcyWorkspaceCodec also has an associated ILcdLogListener. This log listener can be used by all ALcyWorkspaceObjectCodecs and ALcyWorkspaceCodecDelegates to report non-fatal error/warning messages. Furthermore, an associated ILcdStatusListener is available to the ALcyWorkspaceObjectCodecs and ALcyWorkspaceCodecDelegates to report about decoding status. They should report the status of the part they are currently decoding/encoding. This allows the framework to provide a global progress monitor and a progress monitor per task. Note however that a ALcyWorkspaceCodec does not report any progress of the overall decoding/encoding process, this is left to subclasses of ALcyWorkspaceCodec.

Subclasses must implement retrieveInputStream(String) and retrieveOutputStream(String). These methods are responsible for providing access to the actual data storage.

If needed subclasses can store an application specific header, see createApplicationHeader() and checkApplicationHeader(String) for details.

  • Constructor Details

  • Method Details

    • addPropertyChangeListener

      public void addPropertyChangeListener(PropertyChangeListener aListener)
      Description copied from interface: ILcdPropertyChangeSource

      Registers the given PropertyChangeListener to be notified when this object's properties change.

      In case you need to register a listener which keeps a reference to an object with a shorter life-time than this change source, you can use a ALcdWeakPropertyChangeListener instance as property change listener.

      Specified by:
      addPropertyChangeListener in interface ILcdPropertyChangeSource
      Parameters:
      aListener - The listener to be notified
      See Also:
    • addPropertyChangeListener

      public void addPropertyChangeListener(String aProperty, PropertyChangeListener aListener)
      Adds the given listener to the list of listeners, so that it will receive notifications about changes in aProperty.
      Parameters:
      aProperty - The property to watch for changes.
      aListener - The listener to add.
    • removePropertyChangeListener

      public void removePropertyChangeListener(PropertyChangeListener aListener)
      Description copied from interface: ILcdPropertyChangeSource

      De-registers the given PropertyChangeListener from receiving property change events for this object.

      If the listener was added more than once, it will be notified one less time after being removed. If the listener is null, or was never added, no exception is thrown and no action is taken.

      Specified by:
      removePropertyChangeListener in interface ILcdPropertyChangeSource
      Parameters:
      aListener - the listener that should no longer be notified of changes of this object's properties
      See Also:
    • removePropertyChangeListener

      public void removePropertyChangeListener(String aProperty, PropertyChangeListener aListener)
      Removes the given listener for the given property, so that it no longer receives those change events.
      Parameters:
      aProperty - The property to stop listening to.
      aListener - The listener to remove.
    • firePropertyChange

      protected void firePropertyChange(String aProperty, Object aOldValue, Object aNewValue)
      Fires the given event to the associated listeners.
      Parameters:
      aProperty - The property that was changed.
      aOldValue - The old value.
      aNewValue - The new value.
    • getWorkspaceManager

      public TLcyWorkspaceManager getWorkspaceManager()
      Returns:
      the TLcyWorkspaceManager.
    • getLogListener

      public ILcdLogListener getLogListener()
      Returns the ILcdLogListener to which all ALcyWorkspaceObjectCodecs and ALcyWorkspaceCodecDelegates can report non-fatal error/warning messages that need to be reported to the end user.
      Returns:
      The log listener.
    • setLogListener

      public void setLogListener(ILcdLogListener aLogListener)
      Sets the log listener to which all ALcyWorkspaceObjectCodecs and ALcyWorkspaceCodecDelegates can report non-fatal error/warning messages that need to be reported to the end user.

      This listener is also responsible to inform the user of these messages. It can for example do so by writing them to the console, or by displaying them in a dialog after workspace de/encoding has finished. TLcyWorkspaceManager.addWorkspaceManagerListener(ILcyWorkspaceManagerListener) can be used to detect when workspace de/encoding ends.

      Parameters:
      aLogListener - The log listener.
    • getStatusListener

      public ILcdStatusListener getStatusListener()
      Returns the ILcdStatusListener to which all ALcyWorkspaceObjectCodecs and ALcyWorkspaceCodecDelegates can report their status. This allows the framework to provide a global progress monitor and a progress monitor per task.
      Returns:
      The ILcdStatusListener.
    • setStatusListener

      public void setStatusListener(ILcdStatusListener aStatusListener)
      Sets the ILcdStatusListener to which all ALcyWorkspaceObjectCodecs and ALcyWorkspaceCodecDelegates should report the status of the part they are currently decoding/encoding. It is up to the frontend of workspace management ( TLcyWorkspaceAddon is the default frontend) to provide a ILcdStatusListener that handles the status events of the parts that are decoded. The default ILcdStatusListener just sends these events to ILcyLucyEnv.
      Parameters:
      aStatusListener - The ILcdStatusListener.
    • encodeWorkspace

      public void encodeWorkspace(String aDestinationName) throws IOException, TLcyWorkspaceAbortedException
      Encodes the workspace. This method must never be invoked by user code, instead TLcyWorkspaceManager.encodeWorkspace(String) must be used.

      Encoding the workspace means all registered ALcyWorkspaceCodecDelegates will be asked to encode their part. When a ALcyWorkspaceCodecDelegate is encoding its part, it might need to store an object reference (e.g. a reference to an ILcdGXYLayer). It can do so using encodeReference(Object).

      This method must only be used if canEncodeWorkspace(String) for the same aDestinationName returns true.

      Parameters:
      aDestinationName - The destination name of the workspace. It is up to subclasses to interpret this string, e.g. as a file name.
      Throws:
      IOException - In case of fatal IO failure.
      TLcyWorkspaceAbortedException - In case the operation is aborted, e.g. because the user canceled it.
    • decodeWorkspaceDelegateCodecIDs

      public String[] decodeWorkspaceDelegateCodecIDs(String aSourceName) throws IOException
      Decodes the workspace codec delegate UID's used by the given workspace.
      Parameters:
      aSourceName - The source name of the workspace. It is up to subclasses of this class to interpret this string, e.g. as a file name.
      Returns:
      an array containing the UID's of the workspace codec delegates used in the given workspace
      Throws:
      IOException - if the workspace file is not compatible with this application, or if a fatal I/O exception occurs
      Since:
      2022.1
      See Also:
    • decodeWorkspace

      public void decodeWorkspace(String aSourceName) throws IOException, TLcyWorkspaceAbortedException
      Decodes the workspace. This method must never be invoked by user code, instead TLcyWorkspaceManager.decodeWorkspace(String) must be used.

      Decoding the workspace means all encoded parts will be decoded by the correct ALcyWorkspaceCodecDelegate. The UID is used to find the correct ALcyWorkspaceCodecDelegate. When such an ALcyWorkspaceCodecDelegate is decoding its part, it might need to restore an object reference (e.g. to an ILcdGXYLayer). It can do so using decodeReference(String).

      Parameters:
      aSourceName - The source name of the workspace. It is up to subclasses of this class to interpret this string, e.g. as a file name.
      Throws:
      IOException - In case of fatal IO failure.
      TLcyWorkspaceAbortedException - In case the operation is aborted, e.g. because the user canceled it.
    • checkApplicationHeader

      protected void checkApplicationHeader(String aHeader) throws IOException
      Checks if the given header matches the expected header. This method is invoked during workspace decoding. It allows applications that are built on top of Lucy to avoid reading workspaces that were written by Lucy-as-is. If the given header does not match the expected header, this method can either:
      • Log a warning. E.g. getLogListener().warn( this, "Problem with header" )
      • Throw an IOException to indicate that the workspace cannot be read because the header does not match.
      The default implementation verifies if the given header is null, and throws an IOException if it isn't.

      Note that if this method is overridden, createApplicationHeader() must be overridden as well.

      Parameters:
      aHeader - The header to verify. It was read from the workspace file.
      Throws:
      IOException - In case the header does not match and the workspace can therefore not be read.
    • createApplicationHeader

      protected String createApplicationHeader()
      Creates the application specific header. This method is invoked during workspace encoding and the returned value is stored in all workspaces that are written. It allows applications that are build on top of Lucy to avoid reading workspaces that were written by Lucy-as-is. Note that applications build on top of Lucy can also use this header to store some custom version number.

      The default implementation returns null.

      Note that if this method is overridden, checkApplicationHeader(String) must be overridden as well.

      Returns:
      The application specific header.
    • decodeFromInputStream

      protected void decodeFromInputStream(String aStreamUID, InputStream aInputStream) throws IOException
      Subclasses can use this method to tell this ALcyWorkspaceCodec that the given stream must be decoded immediately. If this method is invoked by a subclass, retrieveInputStream(String) with aStreamUID as the argument will not be invoked anymore.
      Parameters:
      aStreamUID - The UID of the stream.
      aInputStream - The stream to decode now.
      Throws:
      IOException - In case of read problems.
    • getWorkspaceStorageName

      public String getWorkspaceStorageName()
      Returns the current storage name of the workspace, this is the last aSourceName or aDestinationName passed to decodeWorkspace(String) or encodeWorkspace(String).
      Returns:
      the current storage name of the workspace.
      See Also:
    • flushWorkspaceStorageName

      public void flushWorkspaceStorageName()
      Forgets the workspace storage name (e.g. the file name). Typically the workspace storage name is used in a save workspace operation. Hence this method can be useful to avoid overwriting the workspace when saving it, for example when it was loaded from a jar file.
      See Also:
    • canEncodeReference

      public boolean canEncodeReference(Object aObject, Object aParent)
      Returns true if the given aObject can be encoded.
      Parameters:
      aObject - The object to encode.
      aParent - The parent of the object to encode. The parent will often be null, or for example when a domain objects needs to be saved (e.g. a polyline), the parent will normally be the ILcdModel that contains the domain object. The exact value of the parent is defined by the ALcyWorkspaceObjectCodec that will encode the object. Please check the javadoc of the addon responsible for the object to encode or refer to the Lucy Developer Guide for more information.
      Returns:
      True if aObject can be encoded, false otherwise.
    • canEncodeReference

      public boolean canEncodeReference(Object aObject)
      Functionally equivalent to canEncodeReference(aObject, null).
      See Also:
    • encodeReference

      public String encodeReference(Object aObject, Object aParent) throws IOException, TLcyWorkspaceAbortedException
      Encodes the given object. A unique String for the object is returned. The String is unique for one object instance and within one workspace. This means that when the same object instance is encoded multiple times, the same String will be returned. When decoding, this String can be passed to decodeReference(String) to restore the object.

      If canEncodeReference(Object, Object) with the same parameters returns false, this method will return null.

      Parameters:
      aObject - The object to encode.
      aParent - The parent of the object to encode. It must be possible to encode this parent too. So either canEncodeReference( aParent ) must return true, or if encoding the parent requires another parent, the parent must have been encoded already. The parent will often be null, or for example when a domain objects needs to be saved (e.g. a polyline), the parent will normally be the ILcdModel that contains the domain object. The exact value of the parent is defined by the ALcyWorkspaceObjectCodec that will encode the object. Please check the javadoc of the addon responsible for the object to encode or refer to the Lucy Developer Guide for more information.
      Returns:
      A String representing the object. The returned ObjectReferenceUID is unique for one object instance and within one workspace. Returns null if aObject is null.
      Throws:
      IOException - In case of fatal IO failure.
      TLcyWorkspaceAbortedException - In case the operation is aborted, e.g. because the user canceled it.
    • encodeReference

      public String encodeReference(Object aObject) throws IOException
      Functionally equivalent to encodeReference(aObject, null).
      Throws:
      IOException
    • encodeObject

      protected void encodeObject(Object aObject, Object aParent, ALcyWorkspaceObjectCodec aCodec, OutputStream aOut) throws IOException
      Called when an object needs to be encoded in the current workspace. The default implementation simply calls the given codec's encodeObject method. You can override the method if you want to be notified of all the object codecs that are used during workspace encoding.
      Parameters:
      aObject - the object to encode
      aParent - The parent of the object to encode (e.g. the ILcdModel of a domain object), or null if there is no parent. The type and value of the parent is defined by the codec.
      aCodec - the codec delegate that can encode the given object
      aOut - the output stream to encode to
      Throws:
      IOException
      Since:
      2022.1
    • decodeReference

      public Object decodeReference(String aObjectReferenceUID) throws IOException, TLcyWorkspaceAbortedException
      Decodes the given object reference (aObjectReferenceUID) into an actual Object. The given aObjectReferenceUID must be a string returned by encodeReference(Object) during the encoding process.

      One cannot assume that because encoding the reference succeeded, decoding the same reference will work too. The given UID cannot be resolved if its ALcyWorkspaceObjectCodec is not available. This can for example happen when an object was encoded by the codec of the addon the object belongs too, but when - in a next Lucy session - the addon is no longer loaded. Such an object could for example be a shp model decoded by the TLcyDefaultDecodersAddOn. In such a case, null will be returned.

      Parameters:
      aObjectReferenceUID - The object reference to decode.
      Returns:
      Null in case the UID could not be decoded, or if the given aObjectReferenceUID is null. The decoded object otherwise.
      Throws:
      IOException - In case of fatal IO failure.
      TLcyWorkspaceAbortedException - In case the operation is aborted, e.g. because the user canceled it.
    • encodePath

      public String encodePath(String aPath)
      Encodes an (absolute) path (e.g. a directory or a filename) into a String. Subclasses can use this e.g. to store paths relative to the location of the workspace file.

      The default implementation simply returns the given aPath.

      Parameters:
      aPath - The path to encode.
      Returns:
      The encoded path.
    • decodePath

      public String decodePath(String aPath)
      Decodes a String returned by encodePath(String) into the original path name. Subclasses can use this e.g. to store paths relative to the location of the workspace file.

      The default implementation simply returns the given aPath.

      Parameters:
      aPath - The path to decode.
      Returns:
      The decoded path.
    • getParentComponent

      public Component getParentComponent()
      Returns the parent component.
      Returns:
      the parent component.
      See Also:
    • setParentComponent

      public void setParentComponent(Component aParentComponent)
      Sets the parent component. This parent component can be used to e.g. show dialogs during the decoding/encoding process.
      Parameters:
      aParentComponent - The parent component.
      See Also:
    • canDecodeWorkspace

      public abstract boolean canDecodeWorkspace(String aSourceName)
      Returns true if the given aSourceName can be decoded.
      Parameters:
      aSourceName - The source name to check.
      Returns:
      true if the given aSourceName can be decoded.
    • canDecodeWorkspace

      public boolean canDecodeWorkspace(String aSourceName, List<String> aReasonsSFCT)

      Returns true if the given source name can be decoded, false otherwise. The second argument of the method is a list of strings which can be used to pass a description to the caller of this method.
      For example when a workspace cannot be decoded for a certain reason, a description of that reason can be added to the list of strings.

      Parameters:
      aSourceName - The source name to check
      aReasonsSFCT - A side-effect parameter to which this method may add reasons on why it returned true or false
      Returns:
      true if the given source name can be decoded, false otherwise
      Since:
      2015.1
    • canEncodeWorkspace

      public abstract boolean canEncodeWorkspace(String aDestinationName)
      Returns true if the workspace can be encoded to the given aDestinationName
      Parameters:
      aDestinationName - The destination name to check.
      Returns:
      true if the workspace can be encoded to the given aDestinationName
    • retrieveOutputStream

      protected abstract OutputStream retrieveOutputStream(String aUID) throws IOException
      Returns an OutputStream for the given aUID. The returned OutputStream should be a new instance for every UID, as several streams might be open at the same time, and data can be written to any of those open streams. This method is used several times during workspace endoding: the stream for every UID represents one little part of the workspace.

      During decoding, retrieveInputStream(aUID) must return an InputStream that refers to the same data block as the one written to the corresponding OutputStream.

      Subclasses can implement this method in various ways: they can store every stream in a separate file, they can store all streams in one single file, they can store the streams in a database, ...

      Parameters:
      aUID - The identifier to retrieve an OutputStream for.
      Returns:
      The OutputStream
      Throws:
      IOException - In case of fatal IO failure.
      See Also:
    • retrieveInputStream

      protected abstract InputStream retrieveInputStream(String aUID) throws IOException
      Returns an InputStream for the given aUID. The returned InputStream should be a new instance for every UID, as several streams might be open at the same time, and data can be read from any of those open streams. It is assumed that every stream can be read until end of stream. This method is used several times during workspace dedoding: the stream for every UID represents one little part of the workspace.

      This method must return an InputStream referring to the same data block as the data that was written to the OutputStream created by retrieveOutputStream(String) for the same aUID.

      Subclasses can implement this method in various ways: they can retrieve every stream from a separate file, they can retrieve all streams from one single file, they can retrieve the streams from a database, ...

      Parameters:
      aUID - The identifier to retrieve an OutputStream for.
      Returns:
      The OutputStream
      Throws:
      IOException - In case of fatal IO failure.
      See Also: