diff --git a/.gitignore b/.gitignore
index f5ab8b8..17832f2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,4 @@
target
/.project
+.metadata/
+.recommenders/
\ No newline at end of file
diff --git a/csharp/.classpath b/csharp/.classpath
new file mode 100644
index 0000000..7f0537f
--- /dev/null
+++ b/csharp/.classpath
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/csharp/.gitignore b/csharp/.gitignore
new file mode 100644
index 0000000..174222f
--- /dev/null
+++ b/csharp/.gitignore
@@ -0,0 +1,3 @@
+/OSGI-INF
+/lib
+/.settings
diff --git a/csharp/.project b/csharp/.project
new file mode 100644
index 0000000..47dea4c
--- /dev/null
+++ b/csharp/.project
@@ -0,0 +1,23 @@
+
+
+ csharp
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ org.eclipse.m2e.core.maven2Builder
+
+
+
+
+
+ org.eclipse.jdt.core.javanature
+ org.eclipse.m2e.core.maven2Nature
+
+
diff --git a/csharp/META-INF/.gitignore b/csharp/META-INF/.gitignore
new file mode 100644
index 0000000..4854a41
--- /dev/null
+++ b/csharp/META-INF/.gitignore
@@ -0,0 +1 @@
+/MANIFEST.MF
diff --git a/csharp/pom.xml b/csharp/pom.xml
new file mode 100644
index 0000000..9058c98
--- /dev/null
+++ b/csharp/pom.xml
@@ -0,0 +1,116 @@
+
+ 4.0.0
+ csharp
+ bundle
+
+
+ org.jabylon
+ 1.3.0
+ jabylon-parent
+
+
+ 1.3.0-SNAPSHOT
+
+ Adds support for C# strings XML
+
+
+ https://github.com/jutzig/jabylon-plugins
+ scm:git:https://github.com/jutzig/jabylon-plugins.git
+ scm:git:https://github.com/jutzig/jabylon-plugins.git
+ HEAD
+
+
+
+
+ jabylon
+ http://www.jabylon.org/maven
+
+ true
+
+
+ false
+
+
+
+
+
+
+
+ src/main/java
+
+ **/*.html
+ **/*.properties
+
+
+
+ ./
+
+ plugin.xml
+
+
+
+
+
+
+
+ ${project.groupId}
+ properties
+ ${project.parent.version}
+ provided
+
+
+ ${project.groupId}
+ common
+ ${project.parent.version}
+ provided
+
+
+ ${project.groupId}
+ rest.ui
+ ${project.parent.version}
+ provided
+
+
+
+ org.eclipse.emf
+ cdo
+
+
+ org.apache.wicket
+ wicket-core
+
+
+ org.slf4j
+ slf4j-api
+
+
+ org.eclipse.emf
+ ecore
+
+
+ org.eclipse.emf
+ common
+
+
+ org.eclipse.osgi
+ services
+
+
+ org.eclipse.equinox
+ common
+
+
+ org.eclipse.equinox
+ preferences
+
+
+ org.apache.felix
+ org.apache.felix.scr.annotations
+
+
+
+ junit
+ junit
+
+
+
diff --git a/csharp/src/main/java/org/jabylon/csharp/CSharpConverter.java b/csharp/src/main/java/org/jabylon/csharp/CSharpConverter.java
new file mode 100644
index 0000000..73089c4
--- /dev/null
+++ b/csharp/src/main/java/org/jabylon/csharp/CSharpConverter.java
@@ -0,0 +1,398 @@
+package org.jabylon.csharp;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.StringWriter;
+import java.io.StringReader;
+
+import java.util.Map;
+
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathExpressionException;
+import javax.xml.xpath.XPathFactory;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+
+import org.eclipse.emf.common.util.EList;
+import org.eclipse.emf.common.util.URI;
+import org.jabylon.properties.PropertiesFactory;
+import org.jabylon.properties.PropertiesPackage;
+import org.jabylon.properties.Property;
+import org.jabylon.properties.PropertyAnnotation;
+import org.jabylon.properties.PropertyFile;
+import org.jabylon.properties.types.PropertyAnnotations;
+import org.jabylon.properties.types.PropertyConverter;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Comment;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.w3c.dom.DOMException;
+import org.xml.sax.SAXException;
+import org.xml.sax.InputSource;
+
+public class CSharpConverter implements PropertyConverter{
+
+ private static final String NAME_ATTRIBUTE = "name";
+ private static final String XML_SPACE = "xml:space";
+ private static final String DATA = "data";
+ private static final String COMMENT = "comment";
+ private static final String PRESERVE = "preserve";
+ private static final String VALUE = "value";
+ private static final String TYPE = "type";
+ private static final String SYSTEM_STRING = "System.String";
+ private static final String INVARIANT = "@Invariant"; // denotes non translatable content
+
+ private static final Logger LOG = LoggerFactory.getLogger(CSharpConverter.class);
+
+ /**
+ * allows to disable pretty print for unit tests
+ */
+ private boolean prettyPrint = true;
+
+ private String comment;
+
+ public CSharpConverter(URI resourceLocation, boolean prettyPrint) {
+ this.prettyPrint = prettyPrint;
+ LOG.debug("C#:CSharpConverter1");
+ }
+
+
+ @Override
+ public PropertyFile load(InputStream in, String encoding) throws IOException {
+ LOG.debug("C#:load0, in: " + in.toString());
+ try {
+ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ factory.setIgnoringComments(false);
+ DocumentBuilder builder = factory.newDocumentBuilder();
+ Document result = builder.parse(in);
+ PropertyFile file = PropertiesFactory.eINSTANCE.createPropertyFile();
+
+ Node resources = result.getDocumentElement();
+
+ LOG.debug("C#:load2, resources, TextContent: " + resources.getTextContent());
+
+ NodeList nodes = resources.getChildNodes();
+
+ int nodesLength = nodes.getLength();
+ LOG.debug("C#:load4, nodesLength: " + nodesLength);
+
+ for (int i = nodesLength-1; i >= 0; i--) {
+ Node node = nodes.item(i);
+ LOG.debug("C#:load5, child node_" + i + " Name: " + node.getNodeName() + " Value: " + node.getNodeValue() + " Content:" + node.getTextContent() + " Type: " + node.getNodeType());
+
+ if (true == loadNode(node,file)) {
+ // 5/14/2019: Now we try plan B and replace the values in the xml file therefore there is no removal anymore of the Jabylon treated nodes
+ //resources.removeChild(node);
+ //LOG.info("C#:load6, child node_" + i + " removed. nodes.getLength afterwards: " + nodes.getLength());
+ }
+ }
+
+ // Build a new Document includes the nodes getting treated by Jabylon
+ DocumentBuilderFactory docBuilderFact = DocumentBuilderFactory.newInstance();
+ docBuilderFact.setNamespaceAware(true);
+ DocumentBuilder docBuilder = docBuilderFact.newDocumentBuilder();
+ Document newDoc = docBuilder.newDocument();
+ Node importedNode = newDoc.importNode(resources, true);
+ newDoc.appendChild(importedNode);
+
+ StringWriter sw = new StringWriter();
+ String resFileAsString = "";
+
+ Node firstNode = newDoc.getChildNodes().item(0);
+
+ try {
+ Transformer t = TransformerFactory.newInstance().newTransformer();
+ t.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
+ t.setOutputProperty(OutputKeys.INDENT, "yes");
+ t.transform(new DOMSource(firstNode), new StreamResult(sw));
+ resFileAsString = sw.toString();
+ LOG.debug("C#:load7, resFileAsString: " + resFileAsString);
+ } catch (TransformerException te) {
+ LOG.error("C#:load8, nodeToString Transformer exception: ", te);
+ }
+ file.setLicenseHeader(resFileAsString); // we take the license header to store everything from our .resx
+
+ return file;
+
+ } catch (SAXException e) {
+ throw new IOException(e);
+ } catch (ParserConfigurationException e) {
+ throw new IOException(e);
+ } finally {
+ in.close();
+ }
+ }
+
+
+ /**
+ *
+ * @param node
+ * @param file
+ * @return true: node is provided for translation
+ * false: node is not provided for translation
+ */
+ private boolean loadNode(Node node, PropertyFile file) {
+ String name = node.getNodeName();
+ LOG.debug("C#:loadNode1, Name: " + name);
+
+ if(false == name.equals(DATA)){
+ LOG.debug("C#:loadNode2, NodeName not appropriate");
+ return false;
+ }
+
+ NamedNodeMap namedNodeMap = node.getAttributes();
+ if (null == namedNodeMap) {
+ LOG.error("C#:loadNode3, namedNodeMap is null");
+ return false;
+ }
+
+ Node namedNode = namedNodeMap.getNamedItem(NAME_ATTRIBUTE);
+ if (null == namedNode) {
+ LOG.error("C#:loadNode4, NAME_ATTRIBUTE not found");
+ return false;
+ }
+
+ for (int j=0; j>")) {
+ LOG.debug("C#:loadNode6, node value not appropriate");
+ return false;
+ }
+ if (true == attribute.getNodeName().equals(TYPE)) {
+ if (false == attribute.getNodeValue().equals(SYSTEM_STRING)) {
+ LOG.debug("C#:loadNode7, type attribute found which is not appropriate");
+ return false;
+ }
+ }
+ }
+
+ // search for comment
+ NodeList childNodes = node.getChildNodes();
+
+ int childNodesLength = childNodes.getLength();
+ LOG.debug("C#:loadNode8, nodesLength: " + childNodesLength);
+
+ for (int k = childNodesLength-1; k >= 0; k--) {
+ Node childNode = childNodes.item(k);
+ String nodeName = childNode.getNodeName();
+ String nodeContent = childNode.getTextContent();
+ LOG.debug("C#:loadNode9, childNode_" + k + " Name: " + nodeName + " Value: " + childNode.getNodeValue() + " Content: " + nodeContent + " Type: " + childNode.getNodeType());
+ if (null != nodeName) {
+ if (nodeName.equals(COMMENT)) {
+ LOG.debug("C#:loadNode10, comment found");
+ if (nodeContent.equals(INVARIANT)) { // identifies an entry that is not provided for translation
+ LOG.debug("C#:loadNode11, comment indicates that this entry is not provided for translation, nodeContent: " + nodeContent);
+ return false;
+ }
+ else {
+ comment = nodeContent;
+ }
+ }
+ }
+ }
+
+ String textContentName = namedNode.getTextContent();
+ LOG.debug("C#:loadNode13, textContentName: " + textContentName);
+
+ Property property = PropertiesFactory.eINSTANCE.createProperty();
+ String key = namedNode.getNodeValue();
+ property.setKey(key);
+
+ LOG.debug("C#:loadNode14, provided for translation ");
+
+ Node valueNode = getValueNode(node);
+ if (null != valueNode) {
+ property.setValue(valueNode.getTextContent());
+ }
+
+ if (null != comment) {
+ LOG.debug("C#:loadNode15, comment was set: " + comment);
+ property.setComment(comment);
+ }
+ comment = null;
+ LOG.debug("C#:loadNode16, property to be added");
+ file.getProperties().add(property);
+ return true;
+ }
+
+
+ private Node getValueNode(Node node) {
+ LOG.debug("C#:getValueNode1");
+ Node childNode = getChildNode(node, VALUE);
+ return childNode;
+ }
+
+
+ private Node getCommentNode(Node node) {
+ LOG.debug("C#:getCommentNode1");
+ Node childNode = getChildNode(node, COMMENT);
+ return childNode;
+ }
+
+
+ private Node getChildNode(Node node, String desiredNodeName) {
+ LOG.debug("C#:getChildNode1, desiredNodeName: " + desiredNodeName);
+ NodeList childNodes = node.getChildNodes();
+ if (null != childNodes) {
+ int numberOfChildren = childNodes.getLength();
+ LOG.debug("C#:getChildNode2, number of child nodes: " + numberOfChildren);
+
+ for (int i=0; i properties = file.getProperties();
+ LOG.debug("C#:write9, Count Properties: " + properties.size());
+
+ for (Property property : properties) {
+ LOG.debug("C#:write10, property: " + property.toString());
+
+ try {
+ XPath xPath = XPathFactory.newInstance().newXPath();
+ Node nodeWithValue = GetNodeWithValue(document, property.getKey(), xPath);
+
+ if (null == nodeWithValue) {
+ LOG.debug("C#:write11, node not found");
+ Node rootNode = (Node)xPath.evaluate("/root", document, XPathConstants.NODE);
+
+ Element newElem = document.createElement(DATA);
+ newElem.setAttribute(NAME_ATTRIBUTE, property.getKey());
+ newElem.setAttribute(XML_SPACE, PRESERVE);
+ rootNode.appendChild(newElem);
+ nodeWithValue = GetNodeWithValue(document, property.getKey(), xPath);
+ }
+ LOG.debug("C#:write12, nodeWithValue: " + nodeWithValue);
+
+ LOG.debug("C#:write13, nodeWithValue.getNodeName: " + nodeWithValue.getNodeName() + " nodeWithValue.getTextContent: " + nodeWithValue.getTextContent() + " nodeWithValue.getNodeValue: " + nodeWithValue.getNodeValue() + " nodeWithValue.getNodeType: " + nodeWithValue.getNodeType());
+
+ String newVal = property.getValue();
+ // remove the carriage return:
+ newVal = newVal.replaceAll("\\r", "");
+ Node valueNode = getValueNode(nodeWithValue);
+ if (null != valueNode) {
+ LOG.debug("C#:write14, newVal replaced: " + newVal);
+ valueNode.setTextContent(newVal);
+ }
+ else {
+ Element newElem = document.createElement(VALUE);
+ LOG.debug("C#:write15, value node inserted: " + newVal);
+ newElem.setTextContent(newVal);
+ }
+
+ String newComment = property.getComment();
+ if (null != newComment) {
+ // remove the carriage return:
+ newComment = newComment.replaceAll("\\r", "");
+ LOG.debug("C#:write16, newComment: " + newComment);
+ Node commentNode = getCommentNode(nodeWithValue);
+ if (null != commentNode) {
+ LOG.debug("C#:write17, comment replaced: " + newComment);
+ commentNode.setTextContent(newComment);
+ }
+ else {
+ Node newNode = nodeWithValue.appendChild(document.createElement(COMMENT));
+ LOG.debug("C#:write18, comment inserted");
+ newNode.setTextContent(newComment);
+ }
+ }
+ } catch (Exception e) {
+ LOG.error("C#:write19 ", e);
+ }
+ }
+
+ LOG.debug("C#:write20");
+ TransformerFactory transformerFactory = TransformerFactory.newInstance();
+ Transformer transformer = transformerFactory.newTransformer();
+ if(prettyPrint){
+ LOG.debug("C#:write21");
+ transformer.setOutputProperty(OutputKeys.INDENT, "yes");
+ transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
+ }
+ DOMSource source = new DOMSource(document);
+ StreamResult result = new StreamResult(out);
+
+ LOG.debug("C#:write22");
+ transformer.transform(source, result);
+ LOG.debug("C#:write23");
+ return counter;
+ } catch (ParserConfigurationException e) {
+ throw new IOException(e);
+ } catch (TransformerConfigurationException e) {
+ throw new IOException(e);
+ } catch (TransformerException e) {
+ throw new IOException(e);
+ } finally{
+ out.close();
+ }
+ }
+
+
+ private Node GetNodeWithValue(Document document, String key, XPath xPath) {
+ String searchStr = "//data[@name=\"" + key + "\"]";
+ LOG.debug("C#:GetNodeWithValue1, searchstr: " + searchStr);
+ Node nodeWithValue =null;
+ try {
+ nodeWithValue = (Node)xPath.evaluate(searchStr, document, XPathConstants.NODE);
+ } catch (XPathExpressionException e) {
+ LOG.debug("C#:GetNodeWithValue2 failed with exception: ", e);
+ }
+ return nodeWithValue;
+ }
+
+
+ private boolean isFilled(String s){
+ LOG.debug("C#:isFilled1, s: " + s);
+ return s!=null && !s.isEmpty();
+ }
+}
\ No newline at end of file
diff --git a/csharp/src/main/java/org/jabylon/csharp/CSharpScanner.java b/csharp/src/main/java/org/jabylon/csharp/CSharpScanner.java
new file mode 100644
index 0000000..dafda72
--- /dev/null
+++ b/csharp/src/main/java/org/jabylon/csharp/CSharpScanner.java
@@ -0,0 +1,279 @@
+/**
+ *
+ */
+package org.jabylon.csharp;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Property;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.wicket.util.crypt.StringUtils;
+import org.eclipse.emf.common.util.URI;
+import org.jabylon.properties.PropertiesFactory;
+import org.jabylon.properties.PropertiesPackage;
+import org.jabylon.properties.ScanConfiguration;
+import org.jabylon.properties.types.PropertyConverter;
+import org.jabylon.properties.types.PropertyScanner;
+import org.jabylon.properties.types.impl.AbstractPropertyScanner;
+//import org.jabylon.security.CommonPermissions;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * adds support for the C# xml format.
+ *
+ * @author c.gromer@seeburger.de
+ *
+ */
+@Component(enabled=true,immediate=true)
+@Service
+public class CSharpScanner implements PropertyScanner {
+
+ @Property(name=PropertyScanner.TYPE, value="CSHARP")
+ public static final String TYPE = "CSHARP";
+
+ private static final String[] DEFAULT_EXCLUDES = {};
+ private static final String[] DEFAULT_INCLUDES = {"**/*.resx"};
+
+ private static final Logger LOG = LoggerFactory.getLogger(CSharpScanner.class);
+
+ public CSharpScanner() {
+ LOG.debug("C#:CSharpScanner1");
+ }
+
+
+ @Override
+ public String[] getDefaultIncludes() {
+ LOG.debug("C#:getDefaultIncludes1");
+ return DEFAULT_INCLUDES;
+ }
+
+ /* (non-Javadoc)
+ * @see org.jabylon.properties.types.PropertyScanner#getDefaultExcludes()
+ */
+ @Override
+ public String[] getDefaultExcludes() {
+ LOG.debug("C#:getDefaultExcludes1");
+ return DEFAULT_EXCLUDES;
+ }
+
+
+ @Override
+ public boolean isTemplate(File propertyFile, String masterLocale) {
+ LOG.debug("C#:isTemplate2 propertyFile: " + propertyFile.getName() + " masterLocale: " + masterLocale);
+
+ if(isResourceFile(propertyFile) && countingPeriodsFullfillsTemplateFileCondition(propertyFile)) {
+ LOG.debug("C#:isTemplate3 propertyFile is a template");
+ return true;
+ }
+
+ LOG.debug("C#:isTemplate4 propertyFile is not a template");
+ return false;
+ }
+
+
+ @Override
+ public boolean isTranslation(File propertyFile, ScanConfiguration config) {
+ String fileName = propertyFile.getName();
+ LOG.debug("C#:isTranslation2 fileName: " + fileName);
+
+ if (isResourceFile(propertyFile) && (countingPeriodsFullfillsTranslationFileCondition(propertyFile))) {
+ LOG.debug("C#:isTranslation3 property file is a translation file");
+ return true;
+ }
+
+ LOG.debug("C#:isTranslation4 property file is not a translation file");
+ return false;
+ }
+
+
+ private boolean countingPeriodsFullfillsTranslationFileCondition(File propertyFile) {
+ //E.g.: Resources.nl.resx is a translation file, Resources.resx is a template file
+ LOG.debug("C#:countingPeriodsFullfillsTranslationFileCondition1: propertyFile: " + propertyFile.getName());
+
+ if (2 == propertyFile.getName().length() - propertyFile.getName().replace(".","").length()) {
+ LOG.debug("C#:countingPeriodsFullfillsTranslationFileCondition2: translation file" );
+ return true;
+ }
+ LOG.debug("C#:countingPeriodsFullfillsTranslationFileCondition3: no translation file" );
+ return false;
+ }
+
+
+ private boolean countingPeriodsFullfillsTemplateFileCondition(File propertyFile) {
+ //E.g.: Resources.nl.resx is a translation file, Resources.resx is a template file
+ boolean bReturn = false;
+ LOG.debug("C#:countingPeriodsFullfillsTemplateFileCondition1: propertyFile: " + propertyFile.getName());
+
+ if (1 == propertyFile.getName().length() - propertyFile.getName().replace(".","").length()) {
+ LOG.debug("C#:countingPeriodsFullfillsTemplateFileCondition2: template" );
+ bReturn = true;
+ }
+
+ LOG.debug("C#:countingPeriodsFullfillsTemplateFileCondition3: bReturn: " + bReturn);
+ return bReturn;
+ }
+
+
+ private boolean isResourceFile(File propertyFile) {
+ LOG.debug("C#:isResourceFile2, propertyFile: " + propertyFile.getName());
+ boolean isResFile = false;
+ if(propertyFile.getName().endsWith(".resx")) {
+ LOG.debug("C#:isResourceFile3: resource file" );
+ isResFile = true;
+ }
+ LOG.debug("C#:isResourceFile4: isResFile: " + isResFile );
+ return isResFile;
+ }
+
+
+ @Override
+ public File findTemplate(File propertyFile, ScanConfiguration config) {
+ LOG.info("C#:findTemplate1");
+ if (isTranslation(propertyFile, config)) {
+ String fileName = propertyFile.getName();
+ LOG.debug("C#:findTemplate2: fileName: " + fileName ); // e.g.: Resources.nl.resx
+ String[] strings = fileName.split(".", -1);
+ if (3 == strings.length) {
+ String templateFile = strings[0] + "." + strings[2]; // e.g.: Resources.resx
+ LOG.debug("C#:findTemplate3: templateFile: " + templateFile);
+ return new File(templateFile);
+ }
+ }
+ LOG.debug("C#:findTemplate4");
+ return null;
+ }
+
+
+ @Override
+ public Map findTranslations(File template, ScanConfiguration config) {
+ LOG.debug("C#:findTranslations1, template: " + template.getName());
+ if (isTemplate(template, "")) {
+ // instead of using org.apache.commons:
+ String templateWithoutExtension = getFirstPart(template);
+ String folder = template.getParent();
+ LOG.debug("C#:findTranslations2: folder: " + folder);
+ File[] files = new File(folder).listFiles();
+ Map translations = new HashMap();
+ for (File file : files) {
+ LOG.debug("C#:findTranslations3: file: " + file.getName());
+ if (templateWithoutExtension.equals(getFirstPart(file))) {
+ LOG.debug("C#:findTranslations4: first part matches");
+ if (isTranslation(file, config)) {
+ LOG.debug("C#:findTranslations5: is translation");
+ translations.put(getLocale(file), file);
+ }
+ }
+ }
+ return translations;
+ }
+ return null;
+ }
+
+
+ private String getFirstPart(File file) {
+ String firstPart = "";
+ String fileParts[] = file.getName().split("\\.");
+
+ if (0 < fileParts.length) {
+ firstPart = fileParts[0];
+ }
+ LOG.debug("C#:getFirstPart, return: " + firstPart);
+ return(firstPart);
+ }
+
+
+ @Override
+ public File computeTranslationPath(File template, Locale templateLocale, Locale translationLocale) {
+ LOG.debug("C#:computeTranslationPath1, template:" + template.getName() + " template.getPath: " + template.getPath());
+ File folder = template.getParentFile(); // template and translation files reside in the same folder
+
+ if (null != folder) {
+ LOG.debug("C#:computeTranslationPath2, folder: " + folder.getName());
+ LOG.debug("C#:computeTranslationPath2.1, folder.getPath: " + folder.getPath());
+ LOG.debug("C#:computeTranslationPath2.2, folder.getParent: " + folder.getParent());
+ }
+
+ return folder;
+ }
+
+
+ @Override
+ public Locale getLocale(File propertyFile) {
+ LOG.debug("C#:getLocale1, propertyFile: " + propertyFile);
+ String propFileName = propertyFile.getName(); // e.g.: dialog.resx (template file), dialog.nl.resx (translation file)
+ LOG.debug("C#:getLocale2, propFileName: " + propFileName);
+
+ String[] splittedPropFileName = propFileName.split("\\.");
+
+ String language = ""; // "en" in "en_US"
+ String culture = ""; // "US" in "en_US" but there is also "Hans" in "zh-Hans"
+ boolean isTemplate = false;
+
+ if (1 == splittedPropFileName.length) {
+ LOG.debug("C#:getLocale3, obviously no property file: " + propFileName);
+ }
+ else if (1 < splittedPropFileName.length) {
+ LOG.debug("C#:getLocale4, splittedPropFileName.length: " + splittedPropFileName.length);
+
+ if (2 == splittedPropFileName.length) {
+ //e.g.: dialog.resx (template file)
+ isTemplate = true;
+ }
+ else if (3 == splittedPropFileName.length){
+ if (2 == splittedPropFileName[splittedPropFileName.length-2].length()) {
+ LOG.debug("C#:getLocale6, only language ");
+ language = splittedPropFileName[splittedPropFileName.length-2];
+ }
+ else {
+ LOG.debug("C#:getLocale7, splittedPropFileName[splittedPropFileName.length-2].substring(2,3): " + splittedPropFileName[splittedPropFileName.length-2].substring(2,3));
+ if ("-".equals(splittedPropFileName[splittedPropFileName.length-2].substring(2,3))) {
+ LOG.debug("C#:getLocale8, language + culture ");
+ language = splittedPropFileName[splittedPropFileName.length-2].substring(0,2); // two chars
+ culture = splittedPropFileName[splittedPropFileName.length-2].substring(3); // can be more than two chars
+ }
+ }
+ }
+ }
+ Locale loc = null;
+
+ if (false == isTemplate) {
+ LOG.debug("C#:getLocale11, no Template");
+ if (0 < language.length()){
+ LOG.debug("C#:getLocale12, isTranslation File, language: " + language + " culture: " + culture);
+ loc = new Locale(language, culture);
+ }
+ }
+ return loc;
+ }
+
+
+ @Override
+ public boolean isBilingual() {
+ LOG.debug("C#:isBilingual1");
+ return false;
+ }
+
+
+ @Override
+ public PropertyConverter createConverter(URI resource) {
+ LOG.debug("C#:createConverter1, resource: " + resource.path());
+ return new CSharpConverter(resource, true);
+ }
+
+
+ @Override
+ public String getEncoding() {
+ String encoding = "UTF-8";
+ LOG.debug("C#:getEncoding1, encoding: " + encoding);
+ return encoding;
+ }
+}
diff --git a/csharp/src/test/Resources/TestRibbon.de.resx b/csharp/src/test/Resources/TestRibbon.de.resx
new file mode 100644
index 0000000..a2e36d3
--- /dev/null
+++ b/csharp/src/test/Resources/TestRibbon.de.resx
@@ -0,0 +1,129 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ BIS FileExchange SyncManager Status-Übersicht
+
+
+ BIS FileExchange Einstellungen...
+
+
+ Über BIS FileExchange...
+
+
\ No newline at end of file
diff --git a/csharp/src/test/Resources/TestRibbon.nl.resx b/csharp/src/test/Resources/TestRibbon.nl.resx
new file mode 100644
index 0000000..5ba5a81
--- /dev/null
+++ b/csharp/src/test/Resources/TestRibbon.nl.resx
@@ -0,0 +1,141 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ BIS FileExchange SyncManager status overzicht
+
+
+ BIS FileExchange voorkeuren …
+
+
+ Over BIS FileExchange ...
+
+
+ BIS FileExchange bestanden of permissies als bijlage toevoegen
+
+
+ Toon permissie instellingen
+
+
+ Toon de eigenschappen van de permissies
+
+
+ Activering van permissive weergave-instellingen.
+
+
\ No newline at end of file
diff --git a/csharp/src/test/Resources/TestRibbon.resx b/csharp/src/test/Resources/TestRibbon.resx
new file mode 100644
index 0000000..4618176
--- /dev/null
+++ b/csharp/src/test/Resources/TestRibbon.resx
@@ -0,0 +1,253 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ BIS FileExchange SyncManager Status Overview
+
+
+ SeeFxSyncManagerStateViewButton
+
+
+ Microsoft.VisualStudio.Tools.Office.Ribbon.View.ButtonView, Microsoft.VisualStudio.Tools.Office.Designer, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+
+ GroupWindow
+
+
+ 0
+
+
+ BIS FileExchange
+ @Invariant
+
+
+ GroupWindow
+
+
+ Microsoft.VisualStudio.Tools.Office.Ribbon.View.GroupView, Microsoft.VisualStudio.Tools.Office.Designer, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+
+ tab1
+
+
+ 0
+
+
+ TabView
+ @Invariant
+
+
+ tab1
+
+
+ Microsoft.VisualStudio.Tools.Office.Ribbon.View.TabView, Microsoft.VisualStudio.Tools.Office.Designer, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+
+ BIS FileExchange Prefs...
+
+
+ SeeFxPrefsButton
+
+
+ Microsoft.VisualStudio.Tools.Office.Ribbon.View.ButtonView, Microsoft.VisualStudio.Tools.Office.Designer, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+
+ SeeFxToolBarButton.Items
+
+
+ 0
+
+
+
+
+ iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
+ YQUAAAAJcEhZcwAACwwAAAsMAT9AIsgAAAB6SURBVDhPpYwBDoRACAPv/5/mUgqxENCNNpkqLezPzMCm
+ 7FdgFH6VSxhWYPxMPCu2+uEJIf5NCyfglN6KU3Dq/kHxjLw6se0gprdCSS1deCuSTdKHR5BHfYY0ky68
+ hq6e6Sw5fdN0pGCFXkOXZhtYo9ewzHdg1f21zP7N7NRI5EAs1QAAAABJRU5ErkJggg==
+
+
+
+ About BIS FileExchange...
+
+
+ SeeFxAboutButton
+
+
+ Microsoft.VisualStudio.Tools.Office.Ribbon.View.ButtonView, Microsoft.VisualStudio.Tools.Office.Designer, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+
+ SeeFxToolBarButton.Items
+
+
+ 1
+
+
+ SeeFxToolBarButton
+
+
+ Microsoft.VisualStudio.Tools.Office.Ribbon.View.SplitButtonView, Microsoft.VisualStudio.Tools.Office.Designer, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+
+ group1
+
+
+ 0
+
+
+ BIS FileExchange
+ @Invariant
+
+
+ group1
+
+
+ Microsoft.VisualStudio.Tools.Office.Ribbon.View.GroupView, Microsoft.VisualStudio.Tools.Office.Designer, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+
+ tab2
+
+
+ 0
+
+
+ TabMail
+ @Invariant
+
+
+ tab2
+
+
+ Microsoft.VisualStudio.Tools.Office.Ribbon.View.TabView, Microsoft.VisualStudio.Tools.Office.Designer, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+
+ True
+
+
+ SeeFxViewRibbon.OfficeMenu
+
+
+ Microsoft.VisualStudio.Tools.Office.Ribbon.View.OfficeMenuView, Microsoft.VisualStudio.Tools.Office.Designer, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+
+ $this
+
+
+ 0
+
+
\ No newline at end of file
diff --git a/csharp/src/test/java/org/jabylon/csharp/CSharpConverterTest.java b/csharp/src/test/java/org/jabylon/csharp/CSharpConverterTest.java
new file mode 100644
index 0000000..bd8f751
--- /dev/null
+++ b/csharp/src/test/java/org/jabylon/csharp/CSharpConverterTest.java
@@ -0,0 +1,63 @@
+package org.jabylon.csharp;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.eclipse.emf.common.util.EList;
+import org.eclipse.emf.common.util.URI;
+import org.jabylon.csharp.CSharpConverter;
+import org.jabylon.properties.Property;
+import org.jabylon.properties.PropertyFile;
+import org.junit.Before;
+import org.junit.Test;
+
+
+public class CSharpConverterTest {
+
+ private CSharpConverter fixture;
+
+ private static final String RESX_FILE = "src/test/resources/TestRibbon.resx";
+
+ @Before
+ public void setup() {
+ fixture = new CSharpConverter(URI.createFileURI(RESX_FILE), false);
+ }
+
+ @Test
+ public void testLoad() throws IOException {
+ PropertyFile result = fixture.load(loadXML(new File(RESX_FILE)), "UTF-8");
+ EList properties = result.getProperties();
+ int sizeOfProperties = properties.size();
+ assertEquals(3, sizeOfProperties);
+ for (int i=0; i translationFiles = fixture.findTranslations(template, null);
+ assertEquals(2, translationFiles.size());
+
+ Locale de = new Locale("de");
+ if (false == translationFiles.containsKey(de))
+ fail("No translation file found that was suitable for German local");
+ Locale nl = new Locale("nl");
+ if (false == translationFiles.containsKey(nl))
+ fail("No translation file found that was suitable for Dutch local");
+
+ File trans_NL = getFileObject(RESX_FILE_NL);
+ if (false == translationFiles.containsValue(trans_NL))
+ fail(" Dutch translation file not found! ");
+
+ File trans_DE = getFileObject(RESX_FILE_DE);
+ if (false == translationFiles.containsValue(trans_DE))
+ fail(" German translation file not found! ");
+ }
+}