
org.xtce.toolkit.XTCEFunctions Maven / Gradle / Ivy
/* Copyright 2015 David Overeem ([email protected])
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.xtce.toolkit;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.List;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import org.omg.space.xtce.AbsoluteTimeDataType;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/** This class is a container to capture some common static functions that are
* applicable throughout the toolkit.
*
* This class is not meant to be constructed, since the functions are static.
*
* @author David Overeem
*
*/
public class XTCEFunctions {
/** Private Constructor
*
* This is to prevent a class of this type from being instantiated. It has
* no non-static functions or data members.
*
*/
private XTCEFunctions() { }
/** Method to register a custom time handler for XTCE AbsoluteTimeDataType
* elements.
*
* @param instance Constructed object of a type that implements the
* interface XTCEAbsoluteTimeType in toolkit.
*
*/
public static void registerAbsoluteTimeHandler( final XTCEAbsoluteTimeType instance ) {
ensureDefaultRegistration();
absTimeHandlers_.add( instance );
}
/** Method to locate a time handler for an XTCE AbsoluteTimeDataType
* element.
*
* Handlers are searched in reverse order of addition, so the newest is
* always checked first.
*
* @param element AbsoluteTimeDataType XML element from an XTCE document.
*
* @return Handler that implements the XTCEAbsoluteTimeType class in this
* toolkit that matches the XML element required contents. If not handler
* exists, then the function throws.
*
* @throws XTCEDatabaseException thrown in the event that a handler is not
* available or not usable for this element.
*
*/
public static XTCEAbsoluteTimeType getAbsoluteTimeHandler( final AbsoluteTimeDataType element ) throws XTCEDatabaseException {
ensureDefaultRegistration();
for ( int iii = absTimeHandlers_.size() - 1; iii >= 0; --iii ) {
try {
if ( absTimeHandlers_.get( iii ).isApplicable( element ) == true ) {
return absTimeHandlers_.get( iii );
}
} catch ( Exception ex ) {
throw new XTCEDatabaseException(
XTCEFunctions.getText( "error_bad_abstime_handler" ) + // NOI18N
" '" +
absTimeHandlers_.get( iii ).getClass().getName() + "': " + // NOI18N
ex.getLocalizedMessage() );
}
}
throw new XTCEDatabaseException(
XTCEFunctions.getText( "error_noabstime_handler" ) ); // NOI18N
}
/** This function converts a byte array to a BitSet suitable for use in the
* container content processing when applying a binary data set.
*
* The BitSet is sequenced where bit 0 is the first bit in the data bytes,
* progressing forward in count until the last bit in the data bytes.
*
* @param bytes byte[] array of raw binary data.
*
* @return BitSet containing the binary data exactly sized to the number
* of bytes, so it will always be a multiple of 8 bits in size.
*
*/
public static BitSet getBitSetFromByteArray( final byte[] bytes ) {
//BitSet bits = new BitSet( bytes.length * 8 );
//for ( int iii = 0; iii < bytes.length; ++iii ) {
// for ( int jjj = 0; jjj < 8; ++jjj ) {
// if ( ( bytes[iii] & ( 1 << ( 7 - jjj ) ) ) != 0 ) {
// bits.set( ( iii * 8 ) + jjj );
// }
// }
//}
//return bits;
return BitSet.valueOf( bytes );
}
/** This function converts a BitSet from the encode functions to a byte
* array in the order it would exist on the wire in that the zeroth index
* is the first byte read/written.
*
* @param rawBits BitSet containing the raw binary data.
*
* @param minBytes int containing the number of bytes expected. Sometimes
* the BitSet can be short or long due to allocations.
*
* @return byte[] containing the data for streaming or printing.
*
*/
public static byte[] getStreamByteArrayFromBitSet( final BitSet rawBits,
final int minBytes ) {
byte[] bytes = new byte[minBytes];
int byteCounter = -1;
int bitSize = rawBits.length();
int remainder;
for ( int iii = 0; iii < ( minBytes * 8 ); ++iii ) {
remainder = iii % 8;
if ( remainder == 0 ) {
++byteCounter;
}
if ( ( iii < bitSize ) && ( rawBits.get( iii ) == true ) ) {
bytes[byteCounter] += 1 << ( 7 - remainder );
}
}
return bytes;
}
/** This function converts a byte array to a BitSet suitable for use in the
* container content processing when applying a binary data set.
*
* The BitSet is sequenced where bit 0 is the first bit in the data bytes,
* progressing forward in count until the last bit in the data bytes.
*
* @param bytes byte[] array of raw binary data.
*
* @return BitSet containing the binary data exactly sized to the number
* of bytes, so it will always be a multiple of 8 bits in size.
*
*/
public static BitSet getBitSetFromStreamByteArray( final byte[] bytes ) {
BitSet bits = new BitSet( bytes.length * 8 );
for ( int iii = 0; iii < bytes.length; ++iii ) {
//System.out.println( "Byte: 0x" + String.format( "%02x", bytes[iii] ) );
for ( int jjj = 0; jjj < 8; ++jjj ) {
if ( ( bytes[iii] & ( 1 << ( 7 - jjj ) ) ) != 0 ) {
bits.set( ( iii * 8 ) + jjj );
//System.out.println( "Setting bit " + Integer.toString( ( iii * 8 ) + jjj ) + " 1" );
} else {
//System.out.println( "Setting bit " + Integer.toString( ( iii * 8 ) + jjj ) + " 0" );
}
}
}
return bits;
}
/** Convert a BitSet object to a hex byte string, ordered from the most
* significant byte to the least significant byte.
*
* The most significant bits are padded with zero in this case when the
* raw size is not on an even 8 bit boundary. This results in the function
* never returning a hex string that is less than 2 characters for each
* byte, with a minimum of 1 byte. A "0x" is prepended.
*
* @param bits BitSet containing an arbitrarily long binary field.
*
* @param minBytes integer containing the minimum number of bytes to output
* on the hex string. This results in 0 padding if the BitSet is too
* small.
*
* @return String containing the hex of the raw value, subject to the
* explanation above associated with this function.
*
*/
public static String bitSetToHex( final BitSet bits, final int minBytes ) {
StringBuilder sb = new StringBuilder( "0x" ); // NOI18N
byte[] bytes = bits.toByteArray();
for ( int iii = minBytes - 1; iii >= 0; --iii ) {
if ( iii < bytes.length ) {
sb.append( String.format( "%02x", bytes[iii] ) ); // NOI18N
} else {
sb.append( "00" ); // NOI18N
}
}
return sb.toString();
}
/** This function mimics the behavior of the ubiquitous filesystem function
* called realpath().
*
* The purpose is to take a filesystem path reference, which may contain
* relative or absolute path, and may also contain the . or .. or multiple
* slashes, and convert that to an absolute path reference. In order to do
* this, the candidate path provided by a user (in this case, an XTCE
* document) and also the current working Space System path are needed.
*
* @param currentPath The current working Space System path from which the
* candidate path reference is to be resolved from.
*
* @param pathReference The candidate path reference, generally from an
* XTCE containerRef, parameterRef, or argumentRef.
*
* @return String that will always contain the absolute path fully resolved
* for the candidate path reference.
*
*/
public static String resolvePathReference( final String currentPath,
final String pathReference ) {
// TODO: This function takes a big performance hit.
StringBuilder fullPath = new StringBuilder();
if ( pathReference.charAt( 0 ) != '/' ) { // NOI18N
fullPath.append( currentPath );
}
String[] fields = pathReference.split( "/" ); // NOI18N
for ( String field : fields ) {
if ( field.equals( ".." ) == true ) {
int index = fullPath.lastIndexOf( "/" ); // NOI18N
if ( index >= 0 ) {
fullPath = fullPath.delete( index, fullPath.length() );
}
} else if ( field.equals( "." ) == true ) { // NOI18N
// do nothing
} else if ( field.isEmpty() == false ) {
fullPath.append( '/' ); // NOI18N
fullPath.append( field );
}
}
String candidate = fullPath.toString();
// this takes half the time called in this function, there should be
// a better way to avoid this
return candidate.replaceAll( "/+", "/" ); // NOI18N
}
/** This function is for stripping the path off an path reference in the
* XTCE data model for containerRef, parameterRef, and argumentRef, amongst
* others.
*
* @param filepath An XTCE style path reference string that contains the
* Space System path leading up to the object of interest, such as a
* Container, Parameter, or Argument, amongst others.
*
* @return String containing just the path to the object name.
*
*/
public static String getPathNameFromReferenceString( final String filepath ) {
int idx = filepath.lastIndexOf( '/' ); // NOI18N
if ( idx == -1 ) {
return ""; // NOI18N
}
return filepath.substring( 0, idx );
}
/** This function is for stripping off object name from an XTCE path
* reference from the data model for containerRef, parameterRef, and
* argumentRef, amongst others.
*
* @param filepath An XTCE style path reference string that contains the
* Space System path leading up to the object of interest, such as a
* Container, Parameter, or Argument, amongst others.
*
* @return String containing just the name of the object.
*
*/
public static String getNameFromPathReferenceString( final String filepath ) {
int idx = filepath.lastIndexOf( '/' ); // NOI18N
if ( idx == -1 ) {
return filepath;
}
return filepath.substring( idx + 1 );
}
/** This function formats the aliases on a named and typed object based on
* some preferences.
*
* The format of the returned string will have each alias space delimited,
* unless there is only one. The "showAllNamespaces" takes precedence
* over the "preferredNamespace". The string will be in the form
* NS1:ALIAS1 NS2:ALIAS2 if the "showAliasNamespaces" option is true.
* Otherwise, it will just be ALIAS1 ALIAS2.
*
* @param parameter XTCENamedObject containing any of the objects that can
* be aliased in the XTCE data model and are supported by this toolkit.
*
* @param showAllNamespaces boolean indicating if all of the namespaces
* should appear in the string.
*
* @param showAliasNamespaces boolean indicating if the alias names should
* be included in the alias string.
*
* @param preferredNamespace String containing the preferred namespace if
* there is more than one defined.
*
* @return String for display purposes.
*
*/
public static String makeAliasDisplayString( final XTCENamedObject parameter,
final boolean showAllNamespaces,
final boolean showAliasNamespaces,
final String preferredNamespace ) {
String myPreferredNamespace = preferredNamespace;
if ( myPreferredNamespace == null ) {
myPreferredNamespace = ""; // NOI18N
}
List aliasList = parameter.getAliasSet();
StringBuilder aliasDisplay = new StringBuilder();
for ( XTCEAlias entry : aliasList ) {
if ( ( showAllNamespaces == true ) ||
( myPreferredNamespace.equals( entry.getNameSpace() ) == true ) ) {
if ( aliasDisplay.length() > 0 ) {
aliasDisplay.append( ' ' ); // NOI18N
}
if ( showAliasNamespaces == true ) {
aliasDisplay.append( entry.getFullAliasName() );
} else {
aliasDisplay.append( entry.getAliasName() );
}
}
}
return aliasDisplay.toString();
}
/** Retrieve a "cleaned up" hex string from a user input hexadecimal field.
*
* The output will have any leading "0x" removed, will be in lower case,
* will contain no whitespace, no commas, no periods, or semicolons. At
* present this function does not guarantee viability of the string from a
* "NumberFormatException" when parsing.
*
* @param inputText String containing user input text.
*
* @return String suitable for parsing the text in base 16.
*
*/
public static String getCleanHexString( final String inputText ) {
String text = inputText.replaceAll( "[ ,;\r\f\n\t.]", "" ); // NOI18N
text = text.toLowerCase();
if ( text.startsWith( "0x" ) == true ) { // NOI18N
text = text.replaceFirst( "0x", "" ); // NOI18N
}
return text;
}
/** Retrieves a byte array from a hexadecimal stream represented by a
* string.
*
* @param hex String containing the user provided hexadecimal string. It
* is recommended to call the "getCleanHexString()" on this text first.
*
* @return byte[] byte array of sufficient size to capture the hexadecimal
* string provided.
*
* @throws NumberFormatException if the string cannot be interpreted as a
* hex string by the Java Integer class.
*
*/
public static byte[] getBytesFromHexString( final String hex ) {
int byteCount = hex.length() / 2;
if ( hex.length() % 2 != 0 ) {
byteCount += 1;
}
byte[] bytes = new byte[byteCount];
for ( int iii = 0; iii < hex.length(); iii += 2 ) {
int startIdx = iii;
int endIdx = iii + 2;
if ( endIdx > hex.length() ) {
endIdx = hex.length();
}
String value = hex.substring( startIdx, endIdx );
if ( value.length() == 1 ) {
value += "0"; // NOI18N
}
bytes[iii / 2] = (byte)Integer.parseInt( value, 16 );
}
return bytes;
}
/** A matcher function that uses the glob style algorithm to determine if
* a candidate string matches a glob style pattern.
*
* This function is to the credit of a suggested algorithm found on many
* stackoverflow.com searches. It is not the authors original work. The
* posting user name is "Mihi" who links to a website at:
* http://schierlm.users.sourceforge.net The stackoverflow thread that
* contains this code is at:
* http://stackoverflow.com/questions/1247772/is-there-an-equivalent-of-java-util-regex-for-glob-type-patterns
*
* @param text String containing the candidate text to match.
*
* @param glob String containing the glob pattern, probably provided by a
* user to match something from a list.
*
* @return boolean indicating if the seatch text matches the glob by the
* glob algorithm rules.
*
*/
public static boolean matchesUsingGlob( final String text, String glob ) {
String rest = null;
int pos = glob.indexOf( '*' ); // NOI18N
if ( pos != -1 ) {
rest = glob.substring( pos + 1 );
glob = glob.substring( 0, pos );
}
if ( glob.length() > text.length() ) {
return false;
}
// handle the part up to the first *
for ( int iii = 0; iii < glob.length(); ++iii ) {
if ( ( glob.charAt( iii ) != '?' ) && // NOI18N
( ! glob.substring( iii, iii + 1 ).equals(text.substring( iii, iii + 1 ) ) ) ) {
return false;
}
}
// recurse for the part after the first *, if any
if ( rest == null ) {
return glob.length() == text.length();
} else {
for ( int iii = glob.length(); iii <= text.length(); ++iii ) {
if ( matchesUsingGlob( text.substring( iii ), rest ) == true ) {
return true;
}
}
return false;
}
}
/** This function supports I18N and L10N for the XTCE toolkit by permitting
* this class to maintain the statics for internationalization and making
* it easy for calling code to get internationalized strings from anywhere.
*
* @param key String containing the key to lookup the desired text message
* from the locale appropriate properties file.
*
* @return String to display in the local language and country preference.
*
*/
public static String getText( final String key ) {
if ( messages_ == null ) {
setLocalePreference( Locale.getDefault() );
}
return messages_.getString( key );
}
/** Sets the user language and country preference so that a ResourceBundle
* can be loaded to assist the getText() function.
*
* @param userLocale Locale object containing the desired language and
* country. A properties file is needed to support the choice.
*
* @return boolean indicating if the preference was valid and could be set.
*
*/
public static boolean setLocalePreference( final Locale userLocale ) {
try {
messages_ = ResourceBundle.getBundle( propLocation_, userLocale );
Locale.setDefault( userLocale );
return true;
} catch ( NullPointerException ex ) {
System.out.println( "Invalid Locale, continuing in US English" ); // NOI18N
} catch ( MissingResourceException ex ) {
System.out.println( "Missing Language Resource Bundle for " + // NOI18N
userLocale.getDisplayName() + ", continuing in US English" ); // NOI18N
}
return false;
}
/** Checks if a Locale preference is supported by this toolkit.
*
* @param userLocale Locale to check to see if a bundle exists.
*
* @return boolean indicating if this Locale is available.
*
*/
public static boolean checkLocaleAvailable( final Locale userLocale ) {
try {
ResourceBundle.getBundle( propLocation_, userLocale );
return true;
} catch ( Exception ex ) {
return false;
}
}
/** Retrieves the internationalized version of the ERROR prefix used for
* exception and log messages.
*
* @return String containing the error text with a colon and space after.
*
*/
public static String generalErrorPrefix() {
return getText( "general_error_caps" ) + ": "; // NOI18N
}
/** Retrieves the internationalized version of the Warning prefix used for
* exception and log messages.
*
* @return String containing the error text with a colon and space after.
*
*/
public static String generalWarningPrefix() {
return getText( "general_warning" ) + ": "; // NOI18N
}
/** Method to format and "beautify" the XML text from a provided DOM
* NodeList object.
*
* This function passes the nodes through the DOM Transformer using a
* stylesheet embedded in this package to format the output.
*
* @param nodes NodeList containing XML nodes to format into ASCII text.
*
* @return String containing the text.
*
* @throws XTCEDatabaseException thrown in the event that the Transformer
* cannot convert the NodeList to XML text.
*
*/
public static String xmlPrettyPrint( final NodeList nodes ) throws XTCEDatabaseException {
InputStream stream = ClassLoader.getSystemResourceAsStream(
"org/xtce/toolkit/prettyprint.xsl" ); // NOI18N
try {
StringBuilder resultsText = new StringBuilder();
Transformer transformer =
TransformerFactory.newInstance()
.newTransformer( new StreamSource( stream ) );
transformer.setOutputProperty( OutputKeys.OMIT_XML_DECLARATION,
"yes" ); // NOI18N
for ( int iii = 0; iii < nodes.getLength(); ++iii ) {
Node item = nodes.item( iii );
switch ( item.getNodeType() ) {
case Node.TEXT_NODE:
resultsText.append( item.getTextContent() );
break;
case Node.ATTRIBUTE_NODE:
resultsText.append( item.getNodeValue() );
break;
default:
ByteArrayOutputStream out = new ByteArrayOutputStream();
Source source = new DOMSource( nodes.item( iii ) );
Result target = new StreamResult( out );
transformer.transform( source, target );
resultsText.append( out.toString() );
}
resultsText.append( System.getProperty( "line.separator" ) ); // NOI18N
}
stream.close();
return resultsText.toString();
} catch ( Exception ex ) {
throw new XTCEDatabaseException( ex );
}
}
/** Method to format and "beautify" the XML text from a provided String
* object containing XML ASCII text.
*
* This function passes the nodes through the DOM Transformer using a
* stylesheet embedded in this package to format the output.
*
* @param xmlText String containing XML text.
*
* @return String containing the text passed through the transformer.
*
* @throws XTCEDatabaseException thrown in the event that the Transformer
* cannot convert the convert the ASCII text.
*
*/
public static String xmlPrettyPrint( final String xmlText ) throws XTCEDatabaseException {
InputStream stream = ClassLoader.getSystemResourceAsStream(
"org/xtce/toolkit/prettyprint.xsl" ); // NOI18N
try {
Transformer transformer =
TransformerFactory.newInstance()
.newTransformer( new StreamSource( stream ) );
transformer.setOutputProperty( OutputKeys.OMIT_XML_DECLARATION,
"yes" ); // NOI18N
ByteArrayInputStream inStream = new ByteArrayInputStream( xmlText.getBytes() );
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
Source source = new StreamSource( inStream );
Result target = new StreamResult( outStream );
transformer.transform( source, target );
stream.close();
return outStream.toString();
} catch ( Exception ex ) {
throw new XTCEDatabaseException( ex );
}
}
/** Creates a string with the current memory usage statistics from the
* Java Virtual Machine.
*
* @return String containing a memory usage description suitable for a log
* line.
*
*/
public static String getMemoryUsageStatistics() {
// this function is basically the contents from a thread on
// stackoverflow.com with the output tweaked.
Runtime runtime = Runtime.getRuntime();
StringBuilder sb = new StringBuilder();
long maxMemory = runtime.maxMemory();
long allocatedMemory = runtime.totalMemory();
long freeMemory = runtime.freeMemory();
long freeAlloc = maxMemory - allocatedMemory;
sb.append( getText( "memstats_title" ) ); // NOI18N
sb.append( ": " ); // NOI18N
sb.append( getText( "memstats_freemem" ) ); // NOI18N
sb.append( " " ); // NOI18N
sb.append( formatMemoryQuantity( freeMemory ) );
sb.append( " " ); // NOI18N
sb.append( getText( "memstats_allocmem" ) ); // NOI18N
sb.append( " " ); // NOI18N
sb.append( formatMemoryQuantity( allocatedMemory ) );
sb.append( " " ); // NOI18N
sb.append( getText( "memstats_maxmem" ) ); // NOI18N
sb.append( " " ); // NOI18N
sb.append( formatMemoryQuantity( maxMemory ) );
sb.append( " " ); // NOI18N
sb.append( getText( "memstats_totalfreemem" ) ); // NOI18N
sb.append( " " ); // NOI18N
sb.append( formatMemoryQuantity( freeMemory + freeAlloc ) );
return sb.toString();
}
/** This function makes the repeat parameter/argument string, taking into
* account the internationalization.
*
* @param count long containing the current item count.
*
* @param total long total number of items.
*
* @return String containing the internationalized text.
*
*/
public static String makeRepeatString( final long count,
final long total ) {
if ( repeatText_.isEmpty() == true ) {
repeatText_ = XTCEFunctions.getText( "general_repeat" ); // NOI18N
ofText_ = XTCEFunctions.getText( "general_of" ); // NOI18N
}
StringBuilder sb = new StringBuilder();
sb.append( repeatText_ );
sb.append( " " ); // NOI18N
sb.append( Long.toString( count ) );
sb.append( " " ); // NOI18N
sb.append( ofText_ );
sb.append( " " ); // NOI18N
sb.append( Long.toString( total ) );
return sb.toString();
}
/** Private method to format the amount of memory provided in bytes to an
* output that used K, M, or G depending on the size of the amount.
*
* This function considers 1024 of each amount to be the cutoff to jump to
* a higher unit. In other words, 1024K will be 1M, 1024M will be 1G.
*
* @param amount long containing the number of bytes.
*
* @return String in the format of a decimal number followed by the letter
* K, M, or G depending on the size of the number of bytes provided.
*
*/
private static String formatMemoryQuantity( final long amount ) {
String unit = "K"; // NOI18N
double quantity = amount / 1024.0;
if ( quantity > ( 1024 * 1024 ) ) {
unit = "G"; // NOI18N
quantity /= ( 1024.0 * 1024.0 );
} else if ( quantity > 1024 ) {
unit = "M"; // NOI18N
quantity /= 1024.0;
}
NumberFormat format = NumberFormat.getInstance();
return format.format( quantity ) + unit;
}
/** Private method to ensure that the default XTCEAbsoluteTimeType handlers
* are registered before searching for one.
*
*/
private static void ensureDefaultRegistration() {
// do not add the TAI variant of the CCSDS CUC yet because it is not
// finished or tested.
if ( absTimeHandlersInitialized_ == false ) {
absTimeHandlers_.add( new XTCEPosixTimeHandler() );
absTimeHandlers_.add( new XTCECcsdsCucTimeHandler( "yyyy-MM-dd HH:mm:ss.SSS", // NOI18N
"GMT", // NOI18N
"1980-01-06", // NOI18N
4,
3 ) );
absTimeHandlersInitialized_ = true;
}
}
private static ResourceBundle messages_ = null;
private static final String propLocation_ =
"org.xtce.toolkit.MessagesBundle"; // NOI18N
private static boolean absTimeHandlersInitialized_ = false;
private static final List absTimeHandlers_ =
new ArrayList<>();
private static String repeatText_ = "";
private static String ofText_ = "";
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy