/**
* Copyright 2014-2017 Linagora, Université Joseph Fourier, Floralis
*
* The present code is developed in the scope of the joint LINAGORA -
* Université Joseph Fourier - Floralis research program and is designated
* as a "Result" pursuant to the terms and conditions of the LINAGORA
* - Université Joseph Fourier - Floralis research program. Each copyright
* holder of Results enumerated here above fully & independently holds complete
* ownership of the complete Intellectual Property rights applicable to the whole
* of said Results, and may freely exploit it in any manner which does not infringe
* the moral rights of the other copyright holders.
*
* 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 net.roboconf.core.model.helpers;
import java.util.AbstractMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import net.roboconf.core.Constants;
import net.roboconf.core.dsl.ParsingConstants;
import net.roboconf.core.model.beans.AbstractApplication;
import net.roboconf.core.model.beans.Component;
import net.roboconf.core.model.beans.ExportedVariable;
import net.roboconf.core.model.beans.ImportedVariable;
import net.roboconf.core.model.beans.Instance;
import net.roboconf.core.utils.Utils;
/**
* Helpers related to variables.
* @author Vincent Zurczak - Linagora
*/
public final class VariableHelpers {
/**
* Private empty constructor.
*/
private VariableHelpers() {
// nothing
}
/**
* Parses a variable name (<facetOrComponentName>.<simpleName>).
* <p>
* All the variables (imported, or exported - after resolution) must be
* prefixed by a component or facet name.
* </p>
* <p>
* If the variable name was not prefixed by a component or a facet name, then
* the couple ( "", < originalVariableName > ) is returned.
* </p>
*
* @param variableName a variable name (not null)
* @return a map entry (key = facet or component name, value = simple name)
*/
public static Map.Entry<String,String> parseVariableName( String variableName ) {
String componentOrFacetName = "", simpleName = variableName;
int index = variableName.indexOf( '.' );
if( index >= 0 ) {
componentOrFacetName = variableName.substring( 0, index ).trim();
simpleName = variableName.substring( index + 1 ).trim();
}
return new AbstractMap.SimpleEntry<>( componentOrFacetName, simpleName );
}
/**
* Parses an exported variable (<variableName> = <defaultValue>).
* <p>
* The equal symbol and default value are optional.
* </p>
*
* @param exportedVariable an exported variable (not null)
* @return a map entry (key = variable name, value = default value)
*/
public static Map.Entry<String,String> parseExportedVariable( String exportedVariable ) {
int index = exportedVariable.indexOf( '=' );
String varName = exportedVariable, defaultValue = null;
if( index > 0 ) {
varName = exportedVariable.substring( 0, index ).trim();
defaultValue = exportedVariable.substring( index + 1 ).trim();
}
return new AbstractMap.SimpleEntry<>( varName, defaultValue );
}
/**
* Parse a list of exported variables.
* @param line a non-null line
* @return a non-null map of exported variables
*/
public static Map<String,ExportedVariable> parseExportedVariables( String line ) {
Pattern randomPattern = Pattern.compile( ParsingConstants.PROPERTY_GRAPH_RANDOM_PATTERN, Pattern.CASE_INSENSITIVE );
Pattern varPattern = Pattern.compile( "([^,=]+)(\\s*=\\s*(\"([^\",]+)\"|([^,]+)))?" );
Map<String,ExportedVariable> result = new LinkedHashMap<> ();
Matcher varMatcher = varPattern.matcher( line );
while( varMatcher.find()) {
String key = varMatcher.group( 1 ).trim();
if( Utils.isEmptyOrWhitespaces( key ))
continue;
String value = null;
if( varMatcher.group( 3 ) != null ) {
// We do not always trim!
// Surrounding white spaces are kept when defined between quotes.
if( varMatcher.group( 5 ) != null )
value = varMatcher.group( 5 ).trim();
else
value = varMatcher.group( 4 );
}
ExportedVariable var = new ExportedVariable();
Matcher m = randomPattern.matcher( key );
if( m.matches()) {
var.setRandom( true );
var.setRawKind( m.group( 1 ));
key = m.group( 2 ).trim();
}
var.setName( key.trim());
if( value != null )
var.setValue( value );
result.put( var.getName(), var );
}
return result;
}
/**
* Finds the component and facet names that prefix the variables an instance exports.
* @param instance an instance
* @return a non-null set with all the component and facet names this instance exports
*/
public static Set<String> findPrefixesForExportedVariables( Instance instance ) {
Set<String> result = new HashSet<> ();
for( String exportedVariableName : InstanceHelpers.findAllExportedVariables( instance ).keySet())
result.add( VariableHelpers.parseVariableName( exportedVariableName ).getKey());
return result;
}
/**
* Finds the component and facet names that prefix the variables an instance imports.
* @param instance an instance
* @return a non-null set with all the component and facet names this instance imports
*/
public static Set<String> findPrefixesForImportedVariables( Instance instance ) {
Set<String> result = new HashSet<> ();
for( ImportedVariable var : ComponentHelpers.findAllImportedVariables( instance.getComponent()).values())
result.add( VariableHelpers.parseVariableName( var.getName()).getKey());
return result;
}
/**
* Finds the component and facet names that prefix the variables an instance requires.
* <p>
* Only the mandatory variables are returned. Optional imports are not considered by this method.
* </p>
*
* @param instance an instance
* @return a non-null set with all the component and facet names this instance imports
*/
public static Set<String> findPrefixesForMandatoryImportedVariables( Instance instance ) {
Set<String> result = new HashSet<> ();
for( ImportedVariable var : ComponentHelpers.findAllImportedVariables( instance.getComponent()).values()) {
if( ! var.isOptional())
result.add( VariableHelpers.parseVariableName( var.getName()).getKey());
}
return result;
}
/**
* Finds the prefixes of the external variables for a given application or template.
* <p>
* If this list is not empty, it means this application depends on
* other ones.
* </p>
*
* @param app an application or an application template
* @return a non-null application
*/
public static Set<String> findPrefixesForExternalImports( AbstractApplication app ) {
Set<String> result = new TreeSet<> ();
for( Component c : ComponentHelpers.findAllComponents( app )) {
for( ImportedVariable var : ComponentHelpers.findAllImportedVariables( c ).values()) {
if( ! var.isExternal())
continue;
String prefix = VariableHelpers.parseVariableName( var.getName()).getKey();
result.add( prefix );
}
}
return result;
}
/**
* Updates the exports of an instance with network values.
* <p>
* For the moment, only IP is supported.
* </p>
*
* @param instanceExports a non-null map of instance exports
* @param ipAddress the IP address to set
*/
static void updateNetworkVariables( Map<String,String> instanceExports, String ipAddress ) {
// Find the keys to update ( xxx.ip )
Set<String> keysToUpdate = new HashSet<> ();
for( Map.Entry<String,String> entry : instanceExports.entrySet()) {
String suffix = parseVariableName( entry.getKey()).getValue();
if( Constants.SPECIFIC_VARIABLE_IP.equalsIgnoreCase( suffix ))
keysToUpdate.add( entry.getKey());
}
// Update them
for( String key : keysToUpdate )
instanceExports.put( key, ipAddress );
}
}