Skip to content
GitLab
Menu
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
Orekit
Orekit
Commits
4719e590
Commit
4719e590
authored
Nov 29, 2022
by
Luc Maisonobe
Browse files
Fixed CCSDS tokens filtering for XML files.
Fixes
#991
parent
1954df30
Pipeline
#2714
passed with stages
in 17 minutes and 20 seconds
Changes
11
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
src/main/java/org/orekit/files/ccsds/ndm/adm/RotationXmlTokenBuilder.java
View file @
4719e590
...
...
@@ -16,15 +16,16 @@
*/
package
org.orekit.files.ccsds.ndm.adm
;
import
java.util.ArrayList
;
import
java.util.Collections
;
import
java.util.List
;
import
java.util.Map
;
import
org.orekit.files.ccsds.utils.lexical.ParseToken
;
import
org.orekit.files.ccsds.utils.lexical.TokenType
;
import
org.orekit.files.ccsds.utils.lexical.XmlTokenBuilder
;
import
org.orekit.utils.units.Unit
;
import
org.orekit.utils.units.UnitsCache
;
import
org.xml.sax.Attributes
;
/** Builder for rotation angles and rates.
* <p>
...
...
@@ -55,26 +56,29 @@ public class RotationXmlTokenBuilder implements XmlTokenBuilder {
/** {@inheritDoc} */
@Override
public
List
<
ParseToken
>
buildTokens
(
final
boolean
startTag
,
final
String
qName
,
final
String
content
,
final
Attributes
attributes
,
public
List
<
ParseToken
>
buildTokens
(
final
boolean
startTag
,
final
boolean
isLeaf
,
final
String
qName
,
final
String
content
,
final
Map
<
String
,
String
>
attributes
,
final
int
lineNumber
,
final
String
fileName
)
{
// get the token name from the first attribute found
String
name
=
attributes
.
get
Value
(
ANGLE
);
String
name
=
attributes
.
get
(
ANGLE
);
if
(
name
==
null
)
{
name
=
attributes
.
get
Value
(
RATE
);
name
=
attributes
.
get
(
RATE
);
}
// elaborate the token type
final
TokenType
type
=
(
content
==
null
)
?
(
startTag
?
TokenType
.
START
:
TokenType
.
STOP
)
:
TokenType
.
ENTRY
;
// get units
final
Unit
units
=
cache
.
getUnits
(
attributes
.
getValue
(
UNITS
));
// final build
final
ParseToken
token
=
new
ParseToken
(
type
,
name
,
content
,
units
,
lineNumber
,
fileName
);
return
Collections
.
singletonList
(
token
);
if
(
startTag
)
{
return
Collections
.
singletonList
(
new
ParseToken
(
TokenType
.
START
,
name
,
content
,
Unit
.
NONE
,
lineNumber
,
fileName
));
}
else
{
final
List
<
ParseToken
>
built
=
new
ArrayList
<>(
2
);
if
(
isLeaf
)
{
// get units
final
Unit
units
=
cache
.
getUnits
(
attributes
.
get
(
UNITS
));
built
.
add
(
new
ParseToken
(
TokenType
.
ENTRY
,
name
,
content
,
units
,
lineNumber
,
fileName
));
}
built
.
add
(
new
ParseToken
(
TokenType
.
STOP
,
name
,
null
,
Unit
.
NONE
,
lineNumber
,
fileName
));
return
built
;
}
}
...
...
src/main/java/org/orekit/files/ccsds/utils/lexical/KvnLexicalAnalyzer.java
View file @
4719e590
...
...
@@ -86,7 +86,7 @@ public class KvnLexicalAnalyzer implements LexicalAnalyzer {
/** Regular expression matching non-comment entry with optional units.
* <p>
* Note than since 2.0, we allow empty values at lexical analysis level and detect them at parsing level
* Note than since
1
2.0, we allow empty values at lexical analysis level and detect them at parsing level
* </p>
*/
private
static
final
Pattern
NON_COMMENT_ENTRY
=
Pattern
.
compile
(
LINE_START
+
NON_COMMENT_KEY
+
OPTIONAL_VALUE
+
UNITS
+
LINE_END
);
...
...
src/main/java/org/orekit/files/ccsds/utils/lexical/MessageVersionXmlTokenBuilder.java
View file @
4719e590
...
...
@@ -19,9 +19,9 @@ package org.orekit.files.ccsds.utils.lexical;
import
java.util.Arrays
;
import
java.util.Collections
;
import
java.util.List
;
import
java.util.Map
;
import
org.orekit.utils.units.Unit
;
import
org.xml.sax.Attributes
;
/** Builder for the root element with CCSDS message version.
* <p>
...
...
@@ -53,13 +53,13 @@ public class MessageVersionXmlTokenBuilder implements XmlTokenBuilder {
/** {@inheritDoc} */
@Override
public
List
<
ParseToken
>
buildTokens
(
final
boolean
startTag
,
final
String
qName
,
final
String
content
,
final
Attributes
attributes
,
public
List
<
ParseToken
>
buildTokens
(
final
boolean
startTag
,
final
boolean
isLeaf
,
final
String
qName
,
final
String
content
,
final
Map
<
String
,
String
>
attributes
,
final
int
lineNumber
,
final
String
fileName
)
{
if
(
startTag
)
{
// we replace the start tag with the message version specification
final
String
id
=
attributes
.
get
Value
(
ID
);
final
String
version
=
attributes
.
get
Value
(
VERSION
);
final
String
id
=
attributes
.
get
(
ID
);
final
String
version
=
attributes
.
get
(
VERSION
);
final
ParseToken
start
=
new
ParseToken
(
TokenType
.
START
,
qName
,
null
,
Unit
.
NONE
,
lineNumber
,
fileName
);
final
ParseToken
entry
=
new
ParseToken
(
TokenType
.
ENTRY
,
id
,
version
,
Unit
.
NONE
,
lineNumber
,
fileName
);
return
Arrays
.
asList
(
start
,
entry
);
...
...
src/main/java/org/orekit/files/ccsds/utils/lexical/RegularXmlTokenBuilder.java
View file @
4719e590
...
...
@@ -16,12 +16,13 @@
*/
package
org.orekit.files.ccsds.utils.lexical
;
import
java.util.ArrayList
;
import
java.util.Collections
;
import
java.util.List
;
import
java.util.Map
;
import
org.orekit.utils.units.Unit
;
import
org.orekit.utils.units.UnitsCache
;
import
org.xml.sax.Attributes
;
/** Regular builder using XML elements names and content for tokens.
* <p>
...
...
@@ -48,22 +49,22 @@ public class RegularXmlTokenBuilder implements XmlTokenBuilder {
/** {@inheritDoc} */
@Override
public
List
<
ParseToken
>
buildTokens
(
final
boolean
startTag
,
final
String
qName
,
final
String
content
,
final
Attributes
attributes
,
public
List
<
ParseToken
>
buildTokens
(
final
boolean
startTag
,
final
boolean
isLeaf
,
final
String
qName
,
final
String
content
,
final
Map
<
String
,
String
>
attributes
,
final
int
lineNumber
,
final
String
fileName
)
{
// elaborate the token type
final
TokenType
tokenType
=
(
content
==
null
)
?
(
startTag
?
TokenType
.
START
:
TokenType
.
STOP
)
:
TokenType
.
ENTRY
;
// get units
final
Unit
units
=
cache
.
getUnits
(
attributes
.
get
Value
(
UNITS
));
// final build
final
ParseToken
token
=
new
ParseToken
(
t
okenType
,
qName
,
content
,
u
nit
s
,
lineNumber
,
fileName
);
return
Collections
.
singletonList
(
token
);
if
(
startTag
)
{
return
Collections
.
singletonList
(
new
ParseToken
(
TokenType
.
START
,
qName
,
content
,
Unit
.
NONE
,
lineNumber
,
fileName
));
}
else
{
final
List
<
ParseToken
>
built
=
new
ArrayList
<>(
2
)
;
if
(
isLeaf
)
{
// get units
final
Unit
units
=
cache
.
getUnits
(
attributes
.
get
(
UNITS
));
built
.
add
(
new
ParseToken
(
TokenType
.
ENTRY
,
qName
,
content
,
units
,
lineNumber
,
fileName
));
}
built
.
add
(
new
ParseToken
(
T
okenType
.
STOP
,
qName
,
null
,
U
nit
.
NONE
,
lineNumber
,
fileName
)
)
;
return
built
;
}
}
...
...
src/main/java/org/orekit/files/ccsds/utils/lexical/UserDefinedXmlTokenBuilder.java
View file @
4719e590
...
...
@@ -16,12 +16,13 @@
*/
package
org.orekit.files.ccsds.utils.lexical
;
import
java.util.ArrayList
;
import
java.util.Collections
;
import
java.util.List
;
import
java.util.Map
;
import
org.orekit.files.ccsds.ndm.odm.UserDefined
;
import
org.orekit.utils.units.Unit
;
import
org.xml.sax.Attributes
;
/** Builder for user-defined parameters.
* <p>
...
...
@@ -43,23 +44,24 @@ public class UserDefinedXmlTokenBuilder implements XmlTokenBuilder {
/** {@inheritDoc} */
@Override
public
List
<
ParseToken
>
buildTokens
(
final
boolean
startTag
,
final
String
qName
,
final
String
content
,
final
Attributes
attributes
,
public
List
<
ParseToken
>
buildTokens
(
final
boolean
startTag
,
final
boolean
isLeaf
,
final
String
qName
,
final
String
content
,
final
Map
<
String
,
String
>
attributes
,
final
int
lineNumber
,
final
String
fileName
)
{
// elaborate the token type
final
TokenType
tokenType
=
(
content
==
null
)
?
(
startTag
?
TokenType
.
START
:
TokenType
.
STOP
)
:
TokenType
.
ENTRY
;
// elaborate name
final
String
name
=
UserDefined
.
USER_DEFINED_PREFIX
+
attributes
.
get
(
UserDefined
.
USER_DEFINED_XML_ATTRIBUTE
);
// final build
final
String
name
=
attributes
.
getValue
(
UserDefined
.
USER_DEFINED_XML_ATTRIBUTE
);
final
ParseToken
token
=
new
ParseToken
(
tokenType
,
UserDefined
.
USER_DEFINED_PREFIX
+
name
,
content
,
Unit
.
NONE
,
lineNumber
,
fileName
);
return
Collections
.
singletonList
(
token
);
if
(
startTag
)
{
return
Collections
.
singletonList
(
new
ParseToken
(
TokenType
.
START
,
name
,
content
,
Unit
.
NONE
,
lineNumber
,
fileName
));
}
else
{
final
List
<
ParseToken
>
built
=
new
ArrayList
<>(
2
);
if
(
isLeaf
)
{
built
.
add
(
new
ParseToken
(
TokenType
.
ENTRY
,
name
,
content
,
Unit
.
NONE
,
lineNumber
,
fileName
));
}
built
.
add
(
new
ParseToken
(
TokenType
.
STOP
,
name
,
null
,
Unit
.
NONE
,
lineNumber
,
fileName
));
return
built
;
}
}
...
...
src/main/java/org/orekit/files/ccsds/utils/lexical/XmlLexicalAnalyzer.java
View file @
4719e590
...
...
@@ -19,6 +19,8 @@ package org.orekit.files.ccsds.utils.lexical;
import
java.io.IOException
;
import
java.io.InputStream
;
import
java.io.Reader
;
import
java.util.Collections
;
import
java.util.HashMap
;
import
java.util.Map
;
import
javax.xml.parsers.ParserConfigurationException
;
...
...
@@ -120,7 +122,17 @@ public class XmlLexicalAnalyzer implements LexicalAnalyzer {
private
String
currentContent
;
/** Attributes of the current element. */
private
Attributes
currentAttributes
;
private
Map
<
String
,
String
>
currentAttributes
;
/** Last processed token qualified name.
* @since 12.0
*/
private
String
lastQname
;
/** Last processed token start/end indicator.
* @since 12.0
*/
private
boolean
lastWasStart
;
/** Simple constructor.
* @param messageParser CCSDS Message parser to use
...
...
@@ -129,6 +141,8 @@ public class XmlLexicalAnalyzer implements LexicalAnalyzer {
this
.
messageParser
=
messageParser
;
this
.
regularBuilder
=
new
RegularXmlTokenBuilder
();
this
.
specialElements
=
messageParser
.
getSpecialXmlElementsBuilders
();
this
.
lastQname
=
""
;
this
.
lastWasStart
=
false
;
}
/** Get a builder for the current element.
...
...
@@ -173,15 +187,26 @@ public class XmlLexicalAnalyzer implements LexicalAnalyzer {
public
void
startElement
(
final
String
uri
,
final
String
localName
,
final
String
qName
,
final
Attributes
attributes
)
{
currentElementName
=
qName
;
currentAttributes
=
attributes
;
currentLineNumber
=
locator
.
getLineNumber
();
currentContent
=
null
;
// save attributes in separate map, to avoid overriding during parsing
if
(
attributes
.
getLength
()
==
0
)
{
currentAttributes
=
Collections
.
emptyMap
();
}
else
{
currentAttributes
=
new
HashMap
<>(
attributes
.
getLength
());
for
(
int
i
=
0
;
i
<
attributes
.
getLength
();
++
i
)
{
currentAttributes
.
put
(
attributes
.
getQName
(
i
),
attributes
.
getValue
(
i
));
}
}
for
(
final
ParseToken
token
:
getBuilder
(
qName
).
buildTokens
(
true
,
qName
,
currentContent
,
currentAttributes
,
buildTokens
(
true
,
false
,
qName
,
currentContent
,
currentAttributes
,
currentLineNumber
,
source
.
getName
()))
{
messageParser
.
process
(
token
);
}
lastQname
=
qName
;
lastWasStart
=
true
;
}
...
...
@@ -194,13 +219,19 @@ public class XmlLexicalAnalyzer implements LexicalAnalyzer {
currentLineNumber
=
locator
.
getLineNumber
();
}
// check if we are parsing the end tag of a leaf element
final
boolean
isLeaf
=
lastWasStart
&&
qName
.
equals
(
lastQname
);
for
(
final
ParseToken
token
:
getBuilder
(
qName
).
buildTokens
(
false
,
qName
,
currentContent
,
currentAttributes
,
buildTokens
(
false
,
isLeaf
,
qName
,
currentContent
,
currentAttributes
,
currentLineNumber
,
source
.
getName
()))
{
messageParser
.
process
(
token
);
}
lastQname
=
qName
;
lastWasStart
=
true
;
currentElementName
=
null
;
currentAttributes
=
null
;
currentLineNumber
=
-
1
;
currentContent
=
null
;
...
...
src/main/java/org/orekit/files/ccsds/utils/lexical/XmlTokenBuilder.java
View file @
4719e590
...
...
@@ -17,8 +17,7 @@
package
org.orekit.files.ccsds.utils.lexical
;
import
java.util.List
;
import
org.xml.sax.Attributes
;
import
java.util.Map
;
/** Builder for building {@link ParseToken} from XML elements.
* <p>
...
...
@@ -39,15 +38,17 @@ public interface XmlTokenBuilder {
/** Create a list of parse tokens.
* @param startTag if true we are parsing the start tag from an XML element
* @param isLeaf if true and startTag is false, we are processing the end tag of a leaf XML element
* @param qName element qualified name
* @param content element content
* @param attributes element attributes
* @param lineNumber number of the line in the CCSDS data message
* @param fileName name of the file
* @return list of parse tokens
* @since 12.0
*/
List
<
ParseToken
>
buildTokens
(
boolean
startTag
,
String
qName
,
String
content
,
Attributes
attributes
,
List
<
ParseToken
>
buildTokens
(
boolean
startTag
,
boolean
isLeaf
,
String
qName
,
String
content
,
Map
<
String
,
String
>
attributes
,
int
lineNumber
,
String
fileName
);
}
src/main/java/org/orekit/files/ccsds/utils/parsing/AbstractMessageParser.java
View file @
4719e590
...
...
@@ -211,7 +211,7 @@ public abstract class AbstractMessageParser<T> implements MessageParser<T> {
if
(
filteredToken
.
getType
()
==
TokenType
.
ENTRY
&&
!
COMMENT
.
equals
(
filteredToken
.
getName
())
&&
filteredToken
.
getRawContent
().
isEmpty
())
{
(
filteredToken
.
getRawContent
()
==
null
||
filteredToken
.
getRawContent
().
isEmpty
())
)
{
// value is empty, which is forbidden by CCSDS standards
throw
new
OrekitException
(
OrekitMessages
.
UNINITIALIZED_VALUE_FOR_KEY
,
filteredToken
.
getName
());
}
...
...
src/site/markdown/architecture/ccsds.md
View file @
4719e590
...
...
@@ -41,7 +41,7 @@ one segment whereas `Oem` may contain several segments).
There are as many sub-packages as there are CCSDS message types, with
intermediate sub-packages for each officially published recommendation:
`org.orekit.files.ccsds.ndm.adm.apm`
,
`org.orekit.files.ccsds.ndm.adm.aem`
,
`org.orekit.files.ccsds.ndm.cdm
.
`
,
`org.orekit.files.ccsds.ndm.odm.opm`
,
`org.orekit.files.ccsds.ndm.cdm`
,
`org.orekit.files.ccsds.ndm.odm.opm`
,
`org.orekit.files.ccsds.ndm.odm.oem`
,
`org.orekit.files.ccsds.ndm.odm.omm`
,
`org.orekit.files.ccsds.ndm.odm.ocm`
, and
`org.orekit.files.ccsds.ndm.tdm`
.
Each contain the logical structures
...
...
@@ -162,7 +162,7 @@ the parsers. There are several use cases for this feature.
the development of this feature) is OMM files in XML format that had an empty
`OBJECT_ID`
,
which is forbidden by CCSDS standard. These non compliant messages could be fixed by
setting a filter that recognizes
`OBJECT_ID`
entries with empty value and replace them
with a value set to
`unknown
'
before passing the changed token back to the parser
with a value set to
`unknown
`
before passing the changed token back to the parser
2) remove unwanted data, for example removing all user-defined data is done by setting
a filter that returns an empty list of tokens when presented with a user-defined entry
3) add data not originally present in the file. For example one could add generated ODM
...
...
src/test/java/org/orekit/files/ccsds/ndm/odm/omm/OmmParserTest.java
View file @
4719e590
...
...
@@ -344,7 +344,7 @@ public class OmmParserTest {
withFilter
(
token
->
{
if
(
"OBJECT_ID"
.
equals
(
token
.
getName
())
&&
(
token
.
getRawContent
()
==
null
||
token
.
getRawContent
().
isEmpty
()))
{
// replace null/empty entries with
"unknown"
// replace null/empty entries with
specified value
return
Collections
.
singletonList
(
new
ParseToken
(
token
.
getType
(),
token
.
getName
(),
replacement
,
token
.
getUnits
(),
token
.
getLineNumber
(),
token
.
getFileName
()));
...
...
@@ -359,6 +359,47 @@ public class OmmParserTest {
}
@Test
public
void
testEmptyObjectIDXml
()
throws
URISyntaxException
{
// test with an OMM file that does not fulfills CCSDS standard and uses an empty OBJECT_ID
String
name
=
"/ccsds/odm/omm/OMM-empty-object-id.xml"
;
final
DataSource
source
=
new
DataSource
(
name
,
()
->
getClass
().
getResourceAsStream
(
name
));
final
OmmParser
parser
=
new
ParserBuilder
().
withMu
(
Constants
.
EIGEN5C_EARTH_MU
).
withMissionReferenceDate
(
new
AbsoluteDate
()).
withDefaultMass
(
1000.0
).
buildOmmParser
();
try
{
parser
.
parseMessage
(
source
);
Assertions
.
fail
(
"an exception should have been thrown"
);
}
catch
(
OrekitException
oe
)
{
oe
.
printStackTrace
();
Assertions
.
assertEquals
(
OrekitMessages
.
UNINITIALIZED_VALUE_FOR_KEY
,
oe
.
getSpecifier
());
Assertions
.
assertEquals
(
"OBJECT_ID"
,
oe
.
getParts
()[
0
]);
}
final
String
replacement
=
"replacement-object-id"
;
final
Omm
omm
=
new
ParserBuilder
().
withMu
(
Constants
.
EIGEN5C_EARTH_MU
).
withMissionReferenceDate
(
new
AbsoluteDate
()).
withDefaultMass
(
1000.0
).
withFilter
(
token
->
{
if
(
"OBJECT_ID"
.
equals
(
token
.
getName
())
&&
(
token
.
getRawContent
()
==
null
||
token
.
getRawContent
().
isEmpty
()))
{
// replace null/empty entries with specified value
return
Collections
.
singletonList
(
new
ParseToken
(
token
.
getType
(),
token
.
getName
(),
replacement
,
token
.
getUnits
(),
token
.
getLineNumber
(),
token
.
getFileName
()));
}
else
{
return
Collections
.
singletonList
(
token
);
}
}).
buildOmmParser
().
parseMessage
(
source
);
// note that object id is always converted to uppercase during parsing
Assertions
.
assertEquals
(
replacement
.
toUpperCase
(),
omm
.
getMetadata
().
getObjectID
());
}
@Test
public
void
testRemoveUserData
()
throws
URISyntaxException
{
final
String
name
=
"/ccsds/odm/omm/OMMExample3.txt"
;
...
...
src/test/resources/ccsds/odm/omm/OMM-empty-object-id.xml
0 → 100644
View file @
4719e590
<?xml version="1.0" encoding="UTF-8"?>
<omm
id=
"CCSDS_OMM_VERS"
version=
"2.0"
>
<header>
<CREATION_DATE>
2021-03-24T23:00:00.000
</CREATION_DATE>
<ORIGINATOR>
CelesTrak
</ORIGINATOR>
</header>
<body>
<segment>
<metadata>
<OBJECT_NAME>
STARLETTE
</OBJECT_NAME>
<OBJECT_ID></OBJECT_ID>
<CENTER_NAME>
EARTH
</CENTER_NAME>
<REF_FRAME>
TEME
</REF_FRAME>
<TIME_SYSTEM>
UTC
</TIME_SYSTEM>
<MEAN_ELEMENT_THEORY>
SGP4
</MEAN_ELEMENT_THEORY>
</metadata>
<data>
<meanElements>
<EPOCH>
2021-03-22T13:21:09.224928
</EPOCH>
<MEAN_MOTION>
13.82309053
</MEAN_MOTION>
<ECCENTRICITY>
.0205751
</ECCENTRICITY>
<INCLINATION>
49.8237
</INCLINATION>
<RA_OF_ASC_NODE>
93.8140
</RA_OF_ASC_NODE>
<ARG_OF_PERICENTER>
224.8348
</ARG_OF_PERICENTER>
<MEAN_ANOMALY>
133.5761
</MEAN_ANOMALY>
</meanElements>
<tleParameters>
<EPHEMERIS_TYPE>
0
</EPHEMERIS_TYPE>
<CLASSIFICATION_TYPE>
U
</CLASSIFICATION_TYPE>
<NORAD_CAT_ID>
7646
</NORAD_CAT_ID>
<ELEMENT_SET_NO>
999
</ELEMENT_SET_NO>
<REV_AT_EPOCH>
32997
</REV_AT_EPOCH>
<BSTAR>
-.47102E-5
</BSTAR>
<MEAN_MOTION_DOT>
-.147E-5
</MEAN_MOTION_DOT>
<MEAN_MOTION_DDOT>
0
</MEAN_MOTION_DDOT>
</tleParameters>
</data>
</segment>
</body>
</omm>
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment