public abstract class ALcyWorkspaceCodec extends Object implements ILcdPropertyChangeSource
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
ALcyWorkspaceCodecDelegate
s and ALcyWorkspaceObjectCodec
s. It also deals with
(circular) references and avoids that objects are encoded twice.
A workspace is basically encoded by asking all registered ALcyWorkspaceCodecDelegate
s 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 ALcyWorkspaceObjectCodec
s and
ALcyWorkspaceCodecDelegate
s to report non-fatal error/warning messages.
Furthermore, an associated ILcdStatusListener
is available to the
ALcyWorkspaceObjectCodec
s and ALcyWorkspaceCodecDelegate
s 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.Modifier | Constructor and Description |
---|---|
protected |
ALcyWorkspaceCodec(ILcyLucyEnv aLucyEnv,
TLcyWorkspaceManager aWorkspaceManager) |
Modifier and Type | Method and Description |
---|---|
void |
addPropertyChangeListener(PropertyChangeListener aListener)
Registers the given
PropertyChangeListener to be notified when this object's
properties change. |
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 . |
abstract boolean |
canDecodeWorkspace(String aSourceName)
Returns true if the given aSourceName can be decoded.
|
boolean |
canDecodeWorkspace(String aSourceName,
List<String> aReasonsSFCT)
Returns true if the given source name can be decoded, false otherwise.
|
boolean |
canEncodeReference(Object aObject)
Functionally equivalent to
canEncodeReference(aObject, null) . |
boolean |
canEncodeReference(Object aObject,
Object aParent)
Returns true if the given aObject can be encoded.
|
abstract boolean |
canEncodeWorkspace(String aDestinationName)
Returns true if the workspace can be encoded to the given
aDestinationName |
protected void |
checkApplicationHeader(String aHeader)
Checks if the given header matches the expected header.
|
protected String |
createApplicationHeader()
Creates the application specific header.
|
protected void |
decodeFromInputStream(String aStreamUID,
InputStream aInputStream)
Subclasses can use this method to tell this
ALcyWorkspaceCodec that
the given stream must be decoded immediately. |
String |
decodePath(String aPath)
Decodes a
String returned by encodePath(String) into the original path
name. |
Object |
decodeReference(String aObjectReferenceUID)
Decodes the given object reference (aObjectReferenceUID) into an actual
Object . |
void |
decodeWorkspace(String aSourceName)
Decodes the workspace.
|
String[] |
decodeWorkspaceDelegateCodecIDs(String aSourceName)
Decodes the workspace codec delegate UID's used by the given workspace.
|
protected void |
encodeObject(Object aObject,
Object aParent,
ALcyWorkspaceObjectCodec aCodec,
OutputStream aOut)
Called when an object needs to be encoded in the current workspace.
|
String |
encodePath(String aPath)
Encodes an (absolute) path (e.g. a directory or a filename) into a String.
|
String |
encodeReference(Object aObject)
Functionally equivalent to
encodeReference(aObject, null) . |
String |
encodeReference(Object aObject,
Object aParent)
Encodes the given object.
|
void |
encodeWorkspace(String aDestinationName)
Encodes the workspace.
|
protected void |
firePropertyChange(String aProperty,
Object aOldValue,
Object aNewValue)
Fires the given event to the associated listeners.
|
void |
flushWorkspaceStorageName()
Forgets the workspace storage name (e.g. the file name).
|
ILcdLogListener |
getLogListener()
Returns the
ILcdLogListener to which all ALcyWorkspaceObjectCodec s
and ALcyWorkspaceCodecDelegate s can report non-fatal error/warning messages that
need to be reported to the end user. |
Component |
getParentComponent()
Returns the parent component.
|
ILcdStatusListener |
getStatusListener()
Returns the
ILcdStatusListener to which all ALcyWorkspaceObjectCodec s
and ALcyWorkspaceCodecDelegate s can report their status. |
TLcyWorkspaceManager |
getWorkspaceManager()
Returns the
TLcyWorkspaceManager . |
String |
getWorkspaceStorageName()
Returns the current storage name of the workspace, this is the last aSourceName or
aDestinationName passed to
decodeWorkspace(String) or
encodeWorkspace(String) . |
void |
removePropertyChangeListener(PropertyChangeListener aListener)
De-registers the given
PropertyChangeListener from receiving property change events
for this object. |
void |
removePropertyChangeListener(String aProperty,
PropertyChangeListener aListener)
Removes the given listener for the given property, so that it no longer receives
those change events.
|
protected abstract InputStream |
retrieveInputStream(String aUID)
Returns an
InputStream for the given aUID. |
protected abstract OutputStream |
retrieveOutputStream(String aUID)
Returns an
OutputStream for the given aUID. |
void |
setLogListener(ILcdLogListener aLogListener)
Sets the log listener to which all
ALcyWorkspaceObjectCodec s and
ALcyWorkspaceCodecDelegate s can report non-fatal error/warning messages that need
to be reported to the end user. |
void |
setParentComponent(Component aParentComponent)
Sets the parent component.
|
void |
setStatusListener(ILcdStatusListener aStatusListener)
Sets the
ILcdStatusListener to which all ALcyWorkspaceObjectCodec s
and ALcyWorkspaceCodecDelegate s should report the status of the part they are
currently decoding/encoding. |
protected ALcyWorkspaceCodec(ILcyLucyEnv aLucyEnv, TLcyWorkspaceManager aWorkspaceManager)
public void addPropertyChangeListener(PropertyChangeListener aListener)
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.
addPropertyChangeListener
in interface ILcdPropertyChangeSource
aListener
- The listener to be notifiedALcdWeakPropertyChangeListener
,
ILcdPropertyChangeSource.removePropertyChangeListener(java.beans.PropertyChangeListener)
public void addPropertyChangeListener(String aProperty, PropertyChangeListener aListener)
aProperty
.aProperty
- The property to watch for changes.aListener
- The listener to add.public void removePropertyChangeListener(PropertyChangeListener aListener)
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.
removePropertyChangeListener
in interface ILcdPropertyChangeSource
aListener
- the listener that should no longer be notified of changes of
this object's propertiesILcdPropertyChangeSource.addPropertyChangeListener(java.beans.PropertyChangeListener)
public void removePropertyChangeListener(String aProperty, PropertyChangeListener aListener)
aProperty
- The property to stop listening to.aListener
- The listener to remove.protected void firePropertyChange(String aProperty, Object aOldValue, Object aNewValue)
aProperty
- The property that was changed.aOldValue
- The old value.aNewValue
- The new value.public TLcyWorkspaceManager getWorkspaceManager()
TLcyWorkspaceManager
.TLcyWorkspaceManager
.public ILcdLogListener getLogListener()
ILcdLogListener
to which all ALcyWorkspaceObjectCodec
s
and ALcyWorkspaceCodecDelegate
s can report non-fatal error/warning messages that
need to be reported to the end user.public void setLogListener(ILcdLogListener aLogListener)
ALcyWorkspaceObjectCodec
s and
ALcyWorkspaceCodecDelegate
s 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.aLogListener
- The log listener.public ILcdStatusListener getStatusListener()
ILcdStatusListener
to which all ALcyWorkspaceObjectCodec
s
and ALcyWorkspaceCodecDelegate
s can report their status. This allows the framework
to provide a global progress monitor and a progress monitor per task.ILcdStatusListener
.public void setStatusListener(ILcdStatusListener aStatusListener)
ILcdStatusListener
to which all ALcyWorkspaceObjectCodec
s
and ALcyWorkspaceCodecDelegate
s 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
.aStatusListener
- The ILcdStatusListener
.public void encodeWorkspace(String aDestinationName) throws IOException, TLcyWorkspaceAbortedException
TLcyWorkspaceManager.encodeWorkspace(String)
must be used.
Encoding the workspace means all registered ALcyWorkspaceCodecDelegate
s 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.aDestinationName
- The destination name of the workspace. It is up to subclasses to
interpret this string, e.g. as a file name.IOException
- In case of fatal IO failure.TLcyWorkspaceAbortedException
- In case the operation is aborted, e.g. because the user canceled
it.public String[] decodeWorkspaceDelegateCodecIDs(String aSourceName) throws IOException
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.IOException
- if the workspace file is not compatible with this application, or if a fatal I/O exception occursALcyWorkspaceCodecDelegate.getUID()
public void decodeWorkspace(String aSourceName) throws IOException, TLcyWorkspaceAbortedException
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)
.
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.IOException
- In case of fatal IO failure.TLcyWorkspaceAbortedException
- In case the operation is aborted, e.g. because the user canceled it.protected void checkApplicationHeader(String aHeader) throws IOException
getLogListener().warn( this, "Problem with header" )
null
, and throws an
IOException if it isn't.
Note that if this method is overridden, createApplicationHeader()
must be overridden
as well.aHeader
- The header to verify. It was read from the workspace file.IOException
- In case the header does not match and the workspace can therefore not be
read.protected String createApplicationHeader()
null
.
Note that if this method is overridden, checkApplicationHeader(String)
must be
overridden as well.protected void decodeFromInputStream(String aStreamUID, InputStream aInputStream) throws IOException
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.aStreamUID
- The UID of the stream.aInputStream
- The stream to decode now.IOException
- In case of read problems.public String getWorkspaceStorageName()
decodeWorkspace(String)
or
encodeWorkspace(String)
.flushWorkspaceStorageName()
public void flushWorkspaceStorageName()
getWorkspaceStorageName()
public boolean canEncodeReference(Object aObject, Object aParent)
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.public boolean canEncodeReference(Object aObject)
canEncodeReference(aObject, null)
.canEncodeReference(Object, Object)
public String encodeReference(Object aObject, Object aParent) throws IOException, TLcyWorkspaceAbortedException
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
.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.String
representing the object. The returned ObjectReferenceUID is
unique for one object instance and within one workspace. Returns null
if aObject
is null
.IOException
- In case of fatal IO failure.TLcyWorkspaceAbortedException
- In case the operation is aborted, e.g. because the user canceled
it.public String encodeReference(Object aObject) throws IOException
encodeReference(aObject, null)
.IOException
protected void encodeObject(Object aObject, Object aParent, ALcyWorkspaceObjectCodec aCodec, OutputStream aOut) throws IOException
aObject
- the object to encodeaParent
- 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 objectaOut
- the output stream to encode toIOException
public Object decodeReference(String aObjectReferenceUID) throws IOException, TLcyWorkspaceAbortedException
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.aObjectReferenceUID
- The object reference to decode.Null
in case the UID could not be decoded, or if the given
aObjectReferenceUID is null
. The decoded object otherwise.IOException
- In case of fatal IO failure.TLcyWorkspaceAbortedException
- In case the operation is aborted, e.g. because the user
canceled it.public String encodePath(String aPath)
aPath
.aPath
- The path to encode.public String decodePath(String aPath)
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
.aPath
- The path to decode.public Component getParentComponent()
setParentComponent(java.awt.Component)
public void setParentComponent(Component aParentComponent)
aParentComponent
- The parent component.getParentComponent()
public abstract boolean canDecodeWorkspace(String aSourceName)
aSourceName
- The source name to check.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.
aSourceName
- The source name to checkaReasonsSFCT
- A side-effect parameter to which this method may add reasons on why it returned true
or false
true
if the given source name can be decoded, false
otherwisepublic abstract boolean canEncodeWorkspace(String aDestinationName)
aDestinationName
aDestinationName
- The destination name to check.aDestinationName
protected abstract OutputStream retrieveOutputStream(String aUID) throws IOException
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,
...aUID
- The identifier to retrieve an OutputStream
for.OutputStream
IOException
- In case of fatal IO failure.retrieveInputStream(String)
protected abstract InputStream retrieveInputStream(String aUID) throws IOException
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, ...aUID
- The identifier to retrieve an OutputStream
for.OutputStream
IOException
- In case of fatal IO failure.retrieveInputStream(String)