catch up with branch daily

Signed-off-by: Ralf Mollik <ramollik@compex-commerce.com>
diff --git a/jenkins.build.config.xml b/jenkins.build.config.xml
index e82076c..15722a6 100644
--- a/jenkins.build.config.xml
+++ b/jenkins.build.config.xml
@@ -20,6 +20,7 @@
                 <jenkins.build.dependency>org.eclipse.osbp.bpmn2.ecore</jenkins.build.dependency>
                 <jenkins.build.dependency>org.eclipse.osbp.dsl</jenkins.build.dependency>
                 <jenkins.build.dependency>org.eclipse.osbp.ecview.addons</jenkins.build.dependency>
+                <jenkins.build.dependency>org.eclipse.osbp.ecview.core</jenkins.build.dependency>
                 <jenkins.build.dependency>org.eclipse.osbp.preferences</jenkins.build.dependency>
                 <jenkins.build.dependency>org.eclipse.osbp.runtime</jenkins.build.dependency>
                 <jenkins.build.dependency>org.eclipse.osbp.ui.api</jenkins.build.dependency>
diff --git a/org.eclipse.osbp.dsl.metadata.service.feature/.settings/org.eclipse.xtend.core.Xtend.prefs b/org.eclipse.osbp.dsl.metadata.service.feature/.settings/org.eclipse.xtend.core.Xtend.prefs
index 0933f8c..19e3115 100644
--- a/org.eclipse.osbp.dsl.metadata.service.feature/.settings/org.eclipse.xtend.core.Xtend.prefs
+++ b/org.eclipse.osbp.dsl.metadata.service.feature/.settings/org.eclipse.xtend.core.Xtend.prefs
@@ -1,4 +1,5 @@
 //outlet.DEFAULT_OUTPUT.sourceFolder.src/test/java.directory=src/test/generated-sources/xtend
+BuilderConfiguration.is_project_specific=true
 eclipse.preferences.version=1
 is_project_specific=true
 outlet.DEFAULT_OUTPUT.hideLocalSyntheticVariables=true
diff --git a/org.eclipse.osbp.dsl.metadata.service/.project b/org.eclipse.osbp.dsl.metadata.service/.project
index 2e86fc6..c77fa23 100644
--- a/org.eclipse.osbp.dsl.metadata.service/.project
+++ b/org.eclipse.osbp.dsl.metadata.service/.project
@@ -31,11 +31,6 @@
 			</arguments>
 		</buildCommand>
 		<buildCommand>
-			<name>org.eclipse.m2e.core.maven2Builder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-		<buildCommand>
 			<name>org.sonarlint.eclipse.core.sonarlintBuilder</name>
 			<arguments>
 			</arguments>
@@ -45,6 +40,11 @@
 			<arguments>
 			</arguments>
 		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.m2e.core.maven2Builder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
 	</buildSpec>
 	<natures>
 		<nature>org.eclipse.xtext.ui.shared.xtextNature</nature>
diff --git a/org.eclipse.osbp.dsl.metadata.service/.settings/org.eclipse.xtend.core.Xtend.prefs b/org.eclipse.osbp.dsl.metadata.service/.settings/org.eclipse.xtend.core.Xtend.prefs
index 0933f8c..19e3115 100644
--- a/org.eclipse.osbp.dsl.metadata.service/.settings/org.eclipse.xtend.core.Xtend.prefs
+++ b/org.eclipse.osbp.dsl.metadata.service/.settings/org.eclipse.xtend.core.Xtend.prefs
@@ -1,4 +1,5 @@
 //outlet.DEFAULT_OUTPUT.sourceFolder.src/test/java.directory=src/test/generated-sources/xtend
+BuilderConfiguration.is_project_specific=true
 eclipse.preferences.version=1
 is_project_specific=true
 outlet.DEFAULT_OUTPUT.hideLocalSyntheticVariables=true
diff --git a/org.eclipse.osbp.dsl.metadata.service/META-INF/MANIFEST.MF b/org.eclipse.osbp.dsl.metadata.service/META-INF/MANIFEST.MF
index 95bc558..4582269 100644
--- a/org.eclipse.osbp.dsl.metadata.service/META-INF/MANIFEST.MF
+++ b/org.eclipse.osbp.dsl.metadata.service/META-INF/MANIFEST.MF
@@ -32,8 +32,10 @@
  org.eclipse.osbp.dsl.dto.xtext;bundle-version="[0.9.0,0.10.0)",
  org.eclipse.osbp.dsl.datatype.xtext;bundle-version="[0.9.0,0.10.0)",
  org.eclipse.osbp.xtext.statemachine;bundle-version="0.9.0",
- org.eclipse.osbp.ecview.dsl;bundle-version="0.9.0",
- org.eclipse.osbp.xtext.signal;bundle-version="0.9.0"
+ org.eclipse.osbp.ecview.dsl;bundle-version="[0.9.0,0.10.0)",
+ org.eclipse.osbp.xtext.signal;bundle-version="[0.9.0,0.10.0)",
+ org.eclipse.osbp.ecview.core.common;bundle-version="[0.9.0,0.10.0)",
+ org.eclipse.osbp.ecview.core.common.model;bundle-version="[0.9.0,0.10.0)"
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
 Bundle-ActivationPolicy: lazy
 Import-Package: javax.validation.constraints,
diff --git a/org.eclipse.osbp.dsl.metadata.service/src/org/eclipse/osbp/dsl/metadata/service/DSLBuilderParticipant.java b/org.eclipse.osbp.dsl.metadata.service/src/org/eclipse/osbp/dsl/metadata/service/DSLBuilderParticipant.java
index 0138b71..aa56d41 100644
--- a/org.eclipse.osbp.dsl.metadata.service/src/org/eclipse/osbp/dsl/metadata/service/DSLBuilderParticipant.java
+++ b/org.eclipse.osbp.dsl.metadata.service/src/org/eclipse/osbp/dsl/metadata/service/DSLBuilderParticipant.java
@@ -6,7 +6,7 @@
  *  are made available under the terms of the Eclipse Public License 2.0        
  *  which accompanies this distribution, and is available at                  
  *  https://www.eclipse.org/legal/epl-2.0/                                 
- *                                 
+ *                                                                            
  *  SPDX-License-Identifier: EPL-2.0                                 
  *                                                                            
  *  Initial contribution:                                                      
@@ -23,6 +23,7 @@
 import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Hashtable;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Locale;
@@ -44,6 +45,8 @@
 import org.eclipse.osbp.dsl.semantic.dto.LDto;
 import org.eclipse.osbp.dsl.semantic.entity.LEntity;
 import org.eclipse.osbp.dsl.xtext.types.bundles.BundleSpaceTypeProvider;
+import org.eclipse.osbp.ecview.core.common.context.IViewContext;
+import org.eclipse.osbp.ecview.core.common.extender.IECViewProviderService;
 import org.eclipse.osbp.ecview.semantic.uimodel.UiModel;
 import org.eclipse.osbp.ecview.semantic.uimodel.UiView;
 import org.eclipse.osbp.preferences.ProductConfiguration;
@@ -76,8 +79,9 @@
 import org.eclipse.osbp.xtext.reportdsl.ReportDSLPackage;
 import org.eclipse.osbp.xtext.reportdsl.ReportPackage;
 import org.eclipse.osbp.xtext.signal.SignalDSLPackage;
+import org.eclipse.osbp.xtext.signal.SignalDefinition;
 import org.eclipse.osbp.xtext.signal.SignalPackage;
-import org.eclipse.osbp.xtext.signal.SignalWatcher;
+import org.eclipse.osbp.xtext.signal.SignalScheduler;
 import org.eclipse.osbp.xtext.table.Table;
 import org.eclipse.osbp.xtext.table.TableDSLPackage;
 import org.eclipse.osbp.xtext.table.TableGrid;
@@ -90,6 +94,9 @@
 import org.eclipse.xtext.util.Pair;
 import org.eclipse.xtext.util.Tuples;
 import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.ServiceReference;
 import org.osgi.framework.ServiceRegistration;
 import org.osgi.framework.wiring.BundleWiring;
 import org.osgi.service.component.ComponentContext;
@@ -109,7 +116,7 @@
 	/** The Constant log. */
 	private static final Logger log = LoggerFactory.getLogger(DSLBuilderParticipant.class);
 
-	/** The metadata builder service. */
+	/** The meta data builder service. */
 	@Inject
 	private IMetadataBuilderService metadataBuilderService;
 
@@ -128,8 +135,17 @@
 	/** The scanned bundles for translations. */
 	private List<Bundle> scannedBundles = new ArrayList<>();
 
+	/** The master bundles for translations. */
+	private List<Bundle> masterBundles = new ArrayList<>();
+	
 	/** The translations. */
 	private HashMap<String, Properties> translations = new HashMap<>();
+	/** The translations source. */
+	private HashMap<String, Properties> translationSource = new HashMap<>();
+	/** The cache for nearest localeTag to a localeString */
+	private HashMap<String, String> localeCache = new HashMap<>();
+	/** to reduce regex matching */
+	private HashMap<String, String> keyCache = new HashMap<>();
 
 	private boolean osbee = false;
 
@@ -199,8 +215,16 @@
 	 *            the suspect
 	 */
 	private void scanTranslationBundles(Bundle suspect) {
-		if (containsHeader(suspect, IExtenderConstants.FACTORY_TRANSLATIONS) && !scannedBundles.contains(suspect)) {
-			loadTranslations(suspect);
+		if ((containsHeader(suspect, IExtenderConstants.FACTORY_TRANSLATIONS) || containsHeader(suspect, IExtenderConstants.FACTORY_MODEL_EXTENDER))
+			&& !scannedBundles.contains(suspect)) {
+			boolean isMasterTranslation=containsHeader(suspect, IExtenderConstants.FACTORY_MASTER_TRANSLATIONS);
+
+			loadTranslations(suspect, isMasterTranslation);
+			
+			if (isMasterTranslation ) {
+				masterBundles.add(suspect);
+			}
+			
 			scannedBundles.add(suspect);
 		}
 	}
@@ -267,8 +291,7 @@
 						}
 						// don't scan translations twice
 						if (!scannedBundles.contains(suspect)) {
-							loadTranslations(suspect);
-							scannedBundles.add(suspect);
+							scanTranslationBundles(suspect);
 						}
 					}
 				}
@@ -283,20 +306,27 @@
 	 * @param suspect
 	 *            the suspect
 	 */
-	void loadTranslations(Bundle suspect) {
+	void loadTranslations(Bundle suspect, boolean isMasterTranslation ) {
 		BundleWiring bundleWiring = suspect.adapt(BundleWiring.class);
 		ClassLoader classLoader = bundleWiring.getClassLoader();
-		// check for duplicates
+		// check for duplicate keys
 		List<String> duplicateKeys = new ArrayList<>();
 		try {
 			final List<String> keys = Collections
 					.list(ResourceBundle.getBundle("i18n.I18N", Locale.ROOT, classLoader).getKeys());
 			for (Entry<Bundle, List<String>> used : usedKeys.entrySet()) {
 				for (String entry : used.getValue()) {
-					if (keys.contains(entry)) {
+					if (keys.contains(entry) && !isMasterTranslation ) {
 						duplicateKeys.add(entry);
-						log.debug("{}  translates the key >{}< but was already translated by {}",
-								suspect.getSymbolicName(), entry, used.getKey().getSymbolicName());
+						if ( log.isDebugEnabled() ) {
+							log.debug("{}  has the key >{}< but is already contained in {}",
+									suspect.getSymbolicName(), entry, used.getKey().getSymbolicName());
+						}
+					} else {
+						if ( log.isDebugEnabled() ) {
+							log.debug("{}  has the key >{}< ",
+									suspect.getSymbolicName(), entry);
+						}
 					}
 				}
 			}
@@ -308,15 +338,24 @@
 				continue;
 			}
 			Properties properties = null;
+			Properties propertiesMasterinfo = null;
+			log.debug("bundle {} loads translations for locale {}", suspect.getSymbolicName(), language);
 			if (translations.containsKey(language)) {
 				properties = translations.get(language);
+			
 			} else {
 				properties = new Properties();
 			}
+			if (translationSource.containsKey(language)) {
+				propertiesMasterinfo = translationSource.get(language);
+				
+			} else {
+				propertiesMasterinfo = new Properties();
+			}
 			try {
 				ResourceBundle resource = ResourceBundle.getBundle("i18n.I18N", Locale.forLanguageTag(language),
 						classLoader);
-				computeTranslations(duplicateKeys, language, properties, resource);
+				computeTranslations(duplicateKeys, language, properties, propertiesMasterinfo, resource, isMasterTranslation);
 			} catch (MissingResourceException e) {
 				log.debug("bundle {} has no resource for locale {}", suspect.getSymbolicName(), language);
 			}
@@ -324,36 +363,75 @@
 	}
 
 	private void computeTranslations(List<String> duplicateKeys, String language, Properties properties, // NOSONAR
-			ResourceBundle resource) {
+			Properties propertiesMasterinfo, ResourceBundle resource, boolean isMasterTranslation) {
+		
 		Enumeration<String> keyIterator = resource.getKeys();
 		while (keyIterator.hasMoreElements()) {
 			String element = keyIterator.nextElement();
 			if (!element.isEmpty()) {
+				
+				if ( propertiesMasterinfo != null && propertiesMasterinfo.get(element) != null ) {
+					// an existing entry is from a master translation file
+					if ( log.isDebugEnabled() ) {
+						log.debug("translation for {} of key >{}< with >{}< ignored. MasterTranslation exists! )",
+								language, element, resource.getString(element) );
+					}
+					continue;
+				}
+				
 				String value = resource.getString(element);
-				if (duplicateKeys.contains(element)) {
-					if (value == null) {
-						value = "";
-					}
-					String prevValue = properties.getProperty(element);
-					if (prevValue == null) {
-						prevValue = "";
-					}
-					int distanceFromKey = StringUtils.getLevenshteinDistance(value, element);
-					int distanceFromPrevious = StringUtils.getLevenshteinDistance(prevValue, element);
-					if (distanceFromKey > 1 && distanceFromKey > distanceFromPrevious) {
+
+				if ( isMasterTranslation ) {
+					// this is a master translation -> add it to the list
+					if (value != null && value.length() != 0 && !value.equals(element)) {
 						properties.put(element, value);
-						log.debug(
-								"translation for {} of key >{}< with >{}< was preferred due to its better levenshtein distance from the key.",
+						propertiesMasterinfo.put(element, "1");
+						log.debug("translation for {} of key >{}< with >{}< added. isMasterTranslation! )",
 								language, element, value);
 					}
-				} else if (value != null && !value.equals(element)) {
+					continue;
+				}
+									
+				if (duplicateKeys.contains(element)) {
+
+					// we have no master translation here, so we have to decide which translation to use
+					
+					// let the empty value be the one with the closest distance, so it will not be used if there is something better! 
+					int distanceFromKey = 0;
+					if ( value != null && value.length() > 0 ) {
+						distanceFromKey = StringUtils.getLevenshteinDistance(value, element);
+					}
+
+					String prevValue = properties.getProperty(element);
+					int distanceFromPrevious = 0;
+					if (prevValue != null && value.length() > 0 ) {
+						distanceFromPrevious = StringUtils.getLevenshteinDistance(prevValue, element);
+					}
+					
+					if (distanceFromKey > 1 && distanceFromKey > distanceFromPrevious) {
+						properties.put(element, value);
+						log.debug( "translation for {} of key >{}< with >{}< was preferred due to its better levenshtein distance from the key.( {} > {} !)",
+								language, element, value, distanceFromKey, distanceFromPrevious);
+					}
+					continue;
+				}
+				
+				if (value != null && value.length() != 0 && !value.equals(element)) {
+					
+					// no master translation, no duplicate key -> just use it.
+					
 					properties.put(element, value);
+					log.debug("translation for {} of key >{}< with >{}< added.  )",
+							language, element, value);
 				}
 			}
 		}
 		if (!properties.isEmpty()) {
 			translations.put(language, properties);
 		}
+		if (!propertiesMasterinfo.isEmpty()) {
+			translationSource.put(language, propertiesMasterinfo);
+		}
 	}
 
 	/**
@@ -377,6 +455,7 @@
 		return false;
 	}
 
+
 	/**
 	 * Gets the extensions.
 	 *
@@ -431,7 +510,6 @@
 	 * Provided as an OSGi service to returnmodels for the given qualified name.
 	 */
 	public class DSLMetadataService implements IDSLMetadataService {
-
 		/*
 		 * (non-Javadoc)
 		 * 
@@ -628,17 +706,29 @@
 			if (localeString == null || key == null) {
 				return key;
 			}
-			String k = I18NKeyGenerator.key(key);
-			// special solution for auto generated dto
-			if (k.endsWith("_dto")) {
-				k = k.substring(0, k.length() - 4);
+			String localeTag = null;
+			if(localeCache.containsKey(localeString)) {
+				localeTag = localeCache.get(localeString);
+			} else {
+				List<Locale.LanguageRange> languageRanges = Locale.LanguageRange.parse(localeString + ";q=1.0");
+				localeTag = Locale.lookupTag(languageRanges, translations.keySet());
+				localeCache.put(localeString, localeTag);
 			}
-			String ranges = localeString + ";q=1.0";
-			List<Locale.LanguageRange> languageRanges = Locale.LanguageRange.parse(ranges);
-			String localeTag = Locale.lookupTag(languageRanges, translations.keySet());
+			
 			if (localeTag != null) {
 				Properties properties = translations.get(localeTag);
 				if (properties != null) {
+					String k = null;
+					if(keyCache.containsKey(key)) {
+						k = keyCache.get(key);
+					} else {
+						k = I18NKeyGenerator.key(key);
+						// special solution for auto generated dto
+						if (k.endsWith("_dto")) {
+							k = k.substring(0, k.length() - 4);
+						}
+						keyCache.put(key, k);
+					}
 					if (properties.containsKey(k)) {
 						return properties.getProperty(k);
 					}
@@ -844,8 +934,8 @@
 				return getTopologyFQN(eObject);
 			} else if (eObject instanceof DatamartDefinition) {
 				return getDatamartFQN(eObject);
-			} else if (eObject instanceof SignalWatcher) {
-				return getSignalWatcherFQN(eObject);
+			} else if (eObject instanceof SignalDefinition) {
+				return getSignalFQN(eObject);
 			}
 			throw new IllegalArgumentException(
 					IDSLMetadataService.ThrowableMessages.NAMING_LOGIC_NOT_REGISTERD + eObject);
@@ -884,7 +974,7 @@
 			} else if (eObject instanceof DataInterchangePackage) {
 				return getDataInterchangeClassName(eObject);
 			} else if (eObject instanceof SignalPackage) {
-				return getSignalWatcherClassName(eObject);
+				return getSignalClassName(eObject);
 			}
 			throw new IllegalArgumentException(
 					IDSLMetadataService.ThrowableMessages.NAMING_LOGIC_NOT_REGISTERD + eObject);
@@ -1065,13 +1155,16 @@
 		 *            the e object
 		 * @return the watcher FQN
 		 */
-		private static String getSignalWatcherFQN(EObject eObject) {
-			SignalWatcher watcher = (SignalWatcher) eObject;
-			if (watcher == null) {
+		private static String getSignalFQN(EObject eObject) {
+			SignalDefinition signal = (SignalDefinition) eObject;
+			if (signal == null) {
 				return "";
 			}
-			SignalPackage pkg = (SignalPackage) watcher.eContainer();
-			return pkg.getName() + "." + watcher.getName();
+			SignalPackage pkg = (SignalPackage) signal.eContainer();
+			if(signal instanceof SignalScheduler){
+				return pkg.getName() + "." + signal.getName() + "Scheduler";			
+		}
+			return pkg.getName() + "." + signal.getName()  + "Watcher";
 		}
 
 		/**
@@ -1125,8 +1218,8 @@
 		 *            the e object
 		 * @return the watcher class name
 		 */
-		private static String getSignalWatcherClassName(EObject eObject) {
-			return getSignalWatcherFQN(eObject) + "Watcher";
+		private static String getSignalClassName(EObject eObject) {
+			return getSignalFQN(eObject);
 		}
 
 		/**