You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@plc4x.apache.org by sr...@apache.org on 2022/05/21 08:16:00 UTC

[plc4x] branch develop updated: feat(codegen): streamline protocol implementations by providing convenience methods

This is an automated email from the ASF dual-hosted git repository.

sruehl pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/plc4x.git


The following commit(s) were added to refs/heads/develop by this push:
     new fa0ce2624c feat(codegen): streamline protocol implementations by providing convenience methods
fa0ce2624c is described below

commit fa0ce2624c64679a5e8fb6dafcb6224487a7a2d2
Author: Sebastian Rühl <sr...@apache.org>
AuthorDate: Sat May 21 10:15:48 2022 +0200

    feat(codegen): streamline protocol implementations by providing convenience methods
---
 .../mspec/parser/MessageFormatListener.java        | 16 ++++-
 .../language/mspec/parser/MessageFormatParser.java | 39 +++++++-----
 .../language/mspec/protocol/ProtocolHelpers.java   | 73 ++++++++++++++++++++++
 .../mspec/protocol/ValidatableTypeContext.java     | 33 ++++------
 .../plc4go/knxnetip/readwrite/ParserHelper.go      |  6 +-
 .../plc4go/knxnetip/readwrite/XmlParserHelper.go   |  6 +-
 .../apache/plc4x/protocol/abeth/ABETHProtocol.java | 18 ++----
 .../plc4x/protocol/ads/ADSDiscoveryProtocol.java   | 20 +++---
 .../org/apache/plc4x/protocol/ads/ADSProtocol.java | 18 ++----
 .../plc4x/protocol/bacnetip/BacNetIpProtocol.java  | 58 ++++-------------
 .../apache/plc4x/protocol/cbus/CBusProtocol.java   | 14 ++---
 .../plc4x/protocol/canopen/CANOpenProtocol.java    | 18 +++---
 .../org/apache/plc4x/protocol/df1/Df1Protocol.java | 14 ++---
 .../org/apache/plc4x/protocol/eip/EipProtocol.java | 14 ++---
 .../plc4x/protocol/firmata/FirmataProtocol.java    | 14 ++---
 .../protocol/genericcan/GenericCANProtocol.java    | 19 +++---
 .../plc4x/protocol/knxnetip/KnxNetIpProtocol.java  | 50 +++------------
 .../plc4x/protocol/modbus/ModbusProtocol.java      | 14 ++---
 .../apache/plc4x/protocol/opcua/OpcuaProtocol.java | 73 +++++-----------------
 .../plc4x/protocol/plc4x/v0/Plc4xProtocol.java     | 17 ++---
 .../plc4x/protocol/profinet/ProfinetProtocol.java  | 18 ++----
 .../org/apache/plc4x/protocol/s7/S7Protocol.java   | 14 ++---
 .../protocol/simulated/SimulatedProtocol.java      | 18 ++----
 .../protocol/socketcan/SocketCANProtocol.java      | 18 +++---
 24 files changed, 257 insertions(+), 345 deletions(-)

diff --git a/code-generation/protocol-base-mspec/src/main/java/org/apache/plc4x/plugins/codegenerator/language/mspec/parser/MessageFormatListener.java b/code-generation/protocol-base-mspec/src/main/java/org/apache/plc4x/plugins/codegenerator/language/mspec/parser/MessageFormatListener.java
index f6882ee125..c292a3a125 100644
--- a/code-generation/protocol-base-mspec/src/main/java/org/apache/plc4x/plugins/codegenerator/language/mspec/parser/MessageFormatListener.java
+++ b/code-generation/protocol-base-mspec/src/main/java/org/apache/plc4x/plugins/codegenerator/language/mspec/parser/MessageFormatListener.java
@@ -29,6 +29,7 @@ import org.apache.plc4x.plugins.codegenerator.language.mspec.model.definitions.D
 import org.apache.plc4x.plugins.codegenerator.language.mspec.model.fields.*;
 import org.apache.plc4x.plugins.codegenerator.language.mspec.model.references.*;
 import org.apache.plc4x.plugins.codegenerator.language.mspec.model.terms.WildcardTerm;
+import org.apache.plc4x.plugins.codegenerator.protocol.TypeContext;
 import org.apache.plc4x.plugins.codegenerator.types.definitions.*;
 import org.apache.plc4x.plugins.codegenerator.types.enums.EnumValue;
 import org.apache.plc4x.plugins.codegenerator.types.fields.ArrayField;
@@ -60,9 +61,9 @@ public class MessageFormatListener extends MSpecBaseListener implements LazyType
 
     protected Map<String, TypeDefinition> types;
 
-    protected Map<String, List<Consumer<TypeDefinition>>> typeDefinitionConsumers = new HashMap<>();
+    protected Map<String, List<Consumer<TypeDefinition>>> typeDefinitionConsumers;
 
-    private Stack<Map<String, Term>> batchSetAttributes = new Stack<>();
+    private final Stack<Map<String, Term>> batchSetAttributes = new Stack<>();
 
     public Deque<List<Field>> getParserContexts() {
         return parserContexts;
@@ -74,11 +75,20 @@ public class MessageFormatListener extends MSpecBaseListener implements LazyType
 
     private String currentTypeName;
 
+    public MessageFormatListener() {
+        types = new HashMap<>();
+        typeDefinitionConsumers = new HashMap<>();
+    }
+
+    public MessageFormatListener(TypeContext exitingTypeContext) {
+        types = new HashMap<>(exitingTypeContext.getTypeDefinitions());
+        typeDefinitionConsumers = new HashMap<>(exitingTypeContext.getUnresolvedTypeReferences());
+    }
+
     @Override
     public void enterFile(MSpecParser.FileContext ctx) {
         parserContexts = new LinkedList<>();
         enumContexts = new LinkedList<>();
-        types = new HashMap<>();
     }
 
     @Override
diff --git a/code-generation/protocol-base-mspec/src/main/java/org/apache/plc4x/plugins/codegenerator/language/mspec/parser/MessageFormatParser.java b/code-generation/protocol-base-mspec/src/main/java/org/apache/plc4x/plugins/codegenerator/language/mspec/parser/MessageFormatParser.java
index 7af48b0679..1c8f1b08cd 100644
--- a/code-generation/protocol-base-mspec/src/main/java/org/apache/plc4x/plugins/codegenerator/language/mspec/parser/MessageFormatParser.java
+++ b/code-generation/protocol-base-mspec/src/main/java/org/apache/plc4x/plugins/codegenerator/language/mspec/parser/MessageFormatParser.java
@@ -23,6 +23,7 @@ import org.antlr.v4.runtime.CommonTokenStream;
 import org.antlr.v4.runtime.tree.ParseTreeWalker;
 import org.apache.plc4x.plugins.codegenerator.language.mspec.MSpecLexer;
 import org.apache.plc4x.plugins.codegenerator.language.mspec.MSpecParser;
+import org.apache.plc4x.plugins.codegenerator.language.mspec.protocol.ValidatableTypeContext;
 import org.apache.plc4x.plugins.codegenerator.protocol.TypeContext;
 import org.apache.plc4x.plugins.codegenerator.types.definitions.TypeDefinition;
 import org.slf4j.Logger;
@@ -40,15 +41,11 @@ public class MessageFormatParser {
 
     private static final Logger LOGGER = LoggerFactory.getLogger(MessageFormatParser.class);
 
-    public TypeContext parse(InputStream source) {
-        return parse(source, new HashMap<>());
+    public ValidatableTypeContext parse(InputStream source) {
+        return parse(source, null);
     }
 
-    public TypeContext parse(InputStream source, Map<String, List<Consumer<TypeDefinition>>> unresolvedTypeReferences) {
-        return parse(source, Collections.emptyMap(), unresolvedTypeReferences);
-    }
-
-    public TypeContext parse(InputStream source, Map<String, TypeDefinition> parsedTypedReferences, Map<String, List<Consumer<TypeDefinition>>> unresolvedTypeReferences) {
+    public ValidatableTypeContext parse(InputStream source, TypeContext existingTypeContext) {
         LOGGER.debug("Parsing: {}", source);
         MSpecLexer lexer;
         try {
@@ -56,19 +53,29 @@ public class MessageFormatParser {
         } catch (IOException e) {
             throw new RuntimeException(e);
         }
-        MessageFormatListener listener = new MessageFormatListener();
-        if (unresolvedTypeReferences != null) {
-            LOGGER.debug("Continue with {} unresolvedTypeReferences", unresolvedTypeReferences.size());
-            listener.typeDefinitionConsumers = unresolvedTypeReferences;
+        MessageFormatListener listener;
+        if (existingTypeContext == null) {
+            listener = new MessageFormatListener();
+        } else {
+            if (LOGGER.isDebugEnabled()) {
+                Map<String, TypeDefinition> exitingTypeDefinitions = existingTypeContext.getTypeDefinitions();
+                if (exitingTypeDefinitions != null) {
+                    LOGGER.debug("Continue with {} exitingTypeDefinitions", exitingTypeDefinitions.size());
+                }
+                Map<String, List<Consumer<TypeDefinition>>> unresolvedTypeReferences = existingTypeContext.getUnresolvedTypeReferences();
+                if (unresolvedTypeReferences != null) {
+                    LOGGER.debug("Continue with {} unresolvedTypeReferences", unresolvedTypeReferences.size());
+                }
+            }
+
+            listener = new MessageFormatListener(existingTypeContext);
         }
+
         new ParseTreeWalker().walk(listener, new MSpecParser(new CommonTokenStream(lexer)).file());
-        if (!parsedTypedReferences.isEmpty()) {
-            LOGGER.info("connecting open consumers with passed types");
-            parsedTypedReferences.forEach(listener::dispatchType);
-        }
         LOGGER.info("Checking for open consumers");
         listener.typeDefinitionConsumers.forEach((key, value) -> LOGGER.warn("{} has {} open consumers", key, value.size()));
-        return new TypeContext() {
+        return new ValidatableTypeContext() {
+
             @Override
             public Map<String, TypeDefinition> getTypeDefinitions() {
                 return listener.types;
diff --git a/code-generation/protocol-base-mspec/src/main/java/org/apache/plc4x/plugins/codegenerator/language/mspec/protocol/ProtocolHelpers.java b/code-generation/protocol-base-mspec/src/main/java/org/apache/plc4x/plugins/codegenerator/language/mspec/protocol/ProtocolHelpers.java
new file mode 100644
index 0000000000..01772302e6
--- /dev/null
+++ b/code-generation/protocol-base-mspec/src/main/java/org/apache/plc4x/plugins/codegenerator/language/mspec/protocol/ProtocolHelpers.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.apache.plc4x.plugins.codegenerator.language.mspec.protocol;
+
+import org.apache.plc4x.plugins.codegenerator.protocol.Protocol;
+import org.apache.plc4x.plugins.codegenerator.types.exceptions.GenerationException;
+
+import java.io.InputStream;
+import java.util.Objects;
+
+public interface ProtocolHelpers extends Protocol {
+
+    /**
+     * Returns a mspec stream by using {@link Protocol#getName()}
+     *
+     * @return the {@link InputStream} of the referenced {@code mspecName}
+     * @throws GenerationException if the mspec can't be found.
+     */
+    default InputStream getMspecStream() throws GenerationException {
+        return getMspecStream(getName());
+    }
+
+    /**
+     * Returns a mspec stream for a give name
+     *
+     * @param mspecName the name without the .mspec extension
+     * @return the {@link InputStream} of the referenced {@code mspecName}
+     * @throws GenerationException if the mspec can't be found.
+     */
+    default InputStream getMspecStream(String mspecName) throws GenerationException {
+        Objects.requireNonNull(mspecName, "mspecName must be set");
+        String versionSubPath = getVersion().map(version -> "/v" + version).orElse("");
+        String packageName = getPackageName();
+        String path = "/protocols/" + packageName + versionSubPath + "/" + mspecName + ".mspec";
+        InputStream inputStream = getClass().getResourceAsStream(path);
+        if (inputStream == null) {
+            throw new GenerationException("Error loading " + mspecName + " schema for protocol '" + getName() + "' (path " + path + ")");
+        }
+        return inputStream;
+    }
+
+    /**
+     * @return {@link Protocol#getName()} in sanitized form
+     */
+    default String getSanitizedName() {
+        return Objects.requireNonNull(getName(), "protocol should return useful value at getName()")
+            // Replace - with emptiness
+            .replaceAll("-", "");
+    }
+
+    /**
+     * @return the package name
+     */
+    default String getPackageName() {
+        return getSanitizedName();
+    }
+}
diff --git a/protocols/c-bus/src/main/java/org/apache/plc4x/protocol/cbus/CBusProtocol.java b/code-generation/protocol-base-mspec/src/main/java/org/apache/plc4x/plugins/codegenerator/language/mspec/protocol/ValidatableTypeContext.java
similarity index 52%
copy from protocols/c-bus/src/main/java/org/apache/plc4x/protocol/cbus/CBusProtocol.java
copy to code-generation/protocol-base-mspec/src/main/java/org/apache/plc4x/plugins/codegenerator/language/mspec/protocol/ValidatableTypeContext.java
index 00556f1938..9fbc7cf4f4 100644
--- a/protocols/c-bus/src/main/java/org/apache/plc4x/protocol/cbus/CBusProtocol.java
+++ b/code-generation/protocol-base-mspec/src/main/java/org/apache/plc4x/plugins/codegenerator/language/mspec/protocol/ValidatableTypeContext.java
@@ -16,33 +16,22 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.plc4x.protocol.cbus;
+package org.apache.plc4x.plugins.codegenerator.language.mspec.protocol;
 
-import org.apache.plc4x.plugins.codegenerator.language.mspec.parser.MessageFormatParser;
-import org.apache.plc4x.plugins.codegenerator.protocol.Protocol;
 import org.apache.plc4x.plugins.codegenerator.protocol.TypeContext;
 import org.apache.plc4x.plugins.codegenerator.types.exceptions.GenerationException;
 
-import java.io.InputStream;
+public interface ValidatableTypeContext extends TypeContext {
 
-public class CBusProtocol implements Protocol {
-
-    @Override
-    public String getName() {
-        return "c-bus";
-    }
-
-    @Override
-    public TypeContext getTypeContext() throws GenerationException {
-        InputStream schemaInputStream = CBusProtocol.class.getResourceAsStream("/protocols/cbus/c-bus.mspec");
-        if(schemaInputStream == null) {
-            throw new GenerationException("Error loading message-format schema for protocol '" + getName() + "'");
+    /**
+     * validates the {@link TypeContext}
+     *
+     * @throws GenerationException if {@link TypeContext}
+     */
+    default void validate() throws GenerationException {
+        // TODO: check that we have at least of parsed type
+        if (getUnresolvedTypeReferences().size() > 0) {
+            throw new GenerationException("Unresolved types left: " + getUnresolvedTypeReferences());
         }
-        TypeContext typeContext = new MessageFormatParser().parse(schemaInputStream);
-        if (typeContext.getUnresolvedTypeReferences().size() > 0) {
-            throw new GenerationException("Unresolved types left: " + typeContext.getUnresolvedTypeReferences());
-        }
-        return typeContext;
     }
-
 }
diff --git a/plc4go/internal/plc4go/knxnetip/readwrite/ParserHelper.go b/plc4go/internal/plc4go/knxnetip/readwrite/ParserHelper.go
index 99591ae09e..d6b016da84 100644
--- a/plc4go/internal/plc4go/knxnetip/readwrite/ParserHelper.go
+++ b/plc4go/internal/plc4go/knxnetip/readwrite/ParserHelper.go
@@ -47,6 +47,9 @@ func (m KnxnetipParserHelper) Parse(typeName string, arguments []string, io util
 		return model.DeviceDescriptorType2Parse(io)
 	case "ChannelInformation":
 		return model.ChannelInformationParse(io)
+	case "KnxDatapoint":
+		datapointType := model.KnxDatapointTypeByName(arguments[0])
+		return model.KnxDatapointParse(io, datapointType)
 	case "DeviceConfigurationAckDataBlock":
 		return model.DeviceConfigurationAckDataBlockParse(io)
 	case "ConnectionRequestInformation":
@@ -130,9 +133,6 @@ func (m KnxnetipParserHelper) Parse(typeName string, arguments []string, io util
 		return model.ApduDataParse(io, dataLength)
 	case "GroupObjectDescriptorRealisationType1":
 		return model.GroupObjectDescriptorRealisationType1Parse(io)
-	case "KnxDatapoint":
-		datapointType := model.KnxDatapointTypeByName(arguments[0])
-		return model.KnxDatapointParse(io, datapointType)
 	}
 	return nil, errors.Errorf("Unsupported type %s", typeName)
 }
diff --git a/plc4go/internal/plc4go/knxnetip/readwrite/XmlParserHelper.go b/plc4go/internal/plc4go/knxnetip/readwrite/XmlParserHelper.go
index fac2b3654c..a7f4039d04 100644
--- a/plc4go/internal/plc4go/knxnetip/readwrite/XmlParserHelper.go
+++ b/plc4go/internal/plc4go/knxnetip/readwrite/XmlParserHelper.go
@@ -58,6 +58,9 @@ func (m KnxnetipXmlParserHelper) Parse(typeName string, xmlString string, parser
 		return model.DeviceDescriptorType2Parse(utils.NewXmlReadBuffer(strings.NewReader(xmlString)))
 	case "ChannelInformation":
 		return model.ChannelInformationParse(utils.NewXmlReadBuffer(strings.NewReader(xmlString)))
+	case "KnxDatapoint":
+		datapointType := model.KnxDatapointTypeByName(parserArguments[0])
+		return model.KnxDatapointParse(utils.NewXmlReadBuffer(strings.NewReader(xmlString)), datapointType)
 	case "DeviceConfigurationAckDataBlock":
 		return model.DeviceConfigurationAckDataBlockParse(utils.NewXmlReadBuffer(strings.NewReader(xmlString)))
 	case "ConnectionRequestInformation":
@@ -146,9 +149,6 @@ func (m KnxnetipXmlParserHelper) Parse(typeName string, xmlString string, parser
 		return model.ApduDataParse(utils.NewXmlReadBuffer(strings.NewReader(xmlString)), dataLength)
 	case "GroupObjectDescriptorRealisationType1":
 		return model.GroupObjectDescriptorRealisationType1Parse(utils.NewXmlReadBuffer(strings.NewReader(xmlString)))
-	case "KnxDatapoint":
-		datapointType := model.KnxDatapointTypeByName(parserArguments[0])
-		return model.KnxDatapointParse(utils.NewXmlReadBuffer(strings.NewReader(xmlString)), datapointType)
 	}
 	return nil, errors.Errorf("Unsupported type %s", typeName)
 }
diff --git a/protocols/ab-eth/src/main/java/org/apache/plc4x/protocol/abeth/ABETHProtocol.java b/protocols/ab-eth/src/main/java/org/apache/plc4x/protocol/abeth/ABETHProtocol.java
index 2c0700960c..6453aabe41 100644
--- a/protocols/ab-eth/src/main/java/org/apache/plc4x/protocol/abeth/ABETHProtocol.java
+++ b/protocols/ab-eth/src/main/java/org/apache/plc4x/protocol/abeth/ABETHProtocol.java
@@ -19,15 +19,13 @@
 package org.apache.plc4x.protocol.abeth;
 
 import org.apache.plc4x.plugins.codegenerator.language.mspec.parser.MessageFormatParser;
+import org.apache.plc4x.plugins.codegenerator.language.mspec.protocol.ProtocolHelpers;
+import org.apache.plc4x.plugins.codegenerator.language.mspec.protocol.ValidatableTypeContext;
 import org.apache.plc4x.plugins.codegenerator.protocol.Protocol;
 import org.apache.plc4x.plugins.codegenerator.protocol.TypeContext;
-import org.apache.plc4x.plugins.codegenerator.types.definitions.TypeDefinition;
 import org.apache.plc4x.plugins.codegenerator.types.exceptions.GenerationException;
 
-import java.io.InputStream;
-import java.util.Map;
-
-public class ABETHProtocol implements Protocol {
+public class ABETHProtocol implements Protocol, ProtocolHelpers {
 
     @Override
     public String getName() {
@@ -36,14 +34,8 @@ public class ABETHProtocol implements Protocol {
 
     @Override
     public TypeContext getTypeContext() throws GenerationException {
-        InputStream schemaInputStream = ABETHProtocol.class.getResourceAsStream("/protocols/abeth/ab-eth.mspec");
-        if(schemaInputStream == null) {
-            throw new GenerationException("Error loading message-format schema for protocol '" + getName() + "'");
-        }
-        TypeContext typeContext = new MessageFormatParser().parse(schemaInputStream);
-        if (typeContext.getUnresolvedTypeReferences().size() > 0) {
-            throw new GenerationException("Unresolved types left: " + typeContext.getUnresolvedTypeReferences());
-        }
+        ValidatableTypeContext typeContext = new MessageFormatParser().parse(getMspecStream());
+        typeContext.validate();
         return typeContext;
     }
 
diff --git a/protocols/ads/src/main/java/org/apache/plc4x/protocol/ads/ADSDiscoveryProtocol.java b/protocols/ads/src/main/java/org/apache/plc4x/protocol/ads/ADSDiscoveryProtocol.java
index a330b86551..e39fa1be47 100644
--- a/protocols/ads/src/main/java/org/apache/plc4x/protocol/ads/ADSDiscoveryProtocol.java
+++ b/protocols/ads/src/main/java/org/apache/plc4x/protocol/ads/ADSDiscoveryProtocol.java
@@ -19,18 +19,18 @@
 package org.apache.plc4x.protocol.ads;
 
 import org.apache.plc4x.plugins.codegenerator.language.mspec.parser.MessageFormatParser;
+import org.apache.plc4x.plugins.codegenerator.language.mspec.protocol.ProtocolHelpers;
+import org.apache.plc4x.plugins.codegenerator.language.mspec.protocol.ValidatableTypeContext;
 import org.apache.plc4x.plugins.codegenerator.protocol.Protocol;
 import org.apache.plc4x.plugins.codegenerator.protocol.TypeContext;
-import org.apache.plc4x.plugins.codegenerator.types.definitions.TypeDefinition;
 import org.apache.plc4x.plugins.codegenerator.types.exceptions.GenerationException;
 
 import java.io.InputStream;
-import java.util.Map;
 
 /**
  * Little helper protocol used to communicate over UDP with Beckhoff hardware.
  */
-public class ADSDiscoveryProtocol implements Protocol {
+public class ADSDiscoveryProtocol implements Protocol, ProtocolHelpers {
 
     @Override
     public String getName() {
@@ -39,15 +39,13 @@ public class ADSDiscoveryProtocol implements Protocol {
 
     @Override
     public TypeContext getTypeContext() throws GenerationException {
-        InputStream schemaInputStream = ADSDiscoveryProtocol.class.getResourceAsStream("/protocols/ads/ads-discovery.mspec");
-        if(schemaInputStream == null) {
-            throw new GenerationException("Error loading message-format schema for protocol '" + getName() + "'");
-        }
-        TypeContext typeContext = new MessageFormatParser().parse(schemaInputStream);
-        if (typeContext.getUnresolvedTypeReferences().size() > 0) {
-            throw new GenerationException("Unresolved types left: " + typeContext.getUnresolvedTypeReferences());
-        }
+        ValidatableTypeContext typeContext = new MessageFormatParser().parse(getMspecStream("ads-discovery"));
+        typeContext.validate();
         return typeContext;
     }
 
+    @Override
+    public String getPackageName() {
+        return "ads";
+    }
 }
diff --git a/protocols/ads/src/main/java/org/apache/plc4x/protocol/ads/ADSProtocol.java b/protocols/ads/src/main/java/org/apache/plc4x/protocol/ads/ADSProtocol.java
index 6168457286..c0d6d876dc 100644
--- a/protocols/ads/src/main/java/org/apache/plc4x/protocol/ads/ADSProtocol.java
+++ b/protocols/ads/src/main/java/org/apache/plc4x/protocol/ads/ADSProtocol.java
@@ -19,15 +19,13 @@
 package org.apache.plc4x.protocol.ads;
 
 import org.apache.plc4x.plugins.codegenerator.language.mspec.parser.MessageFormatParser;
+import org.apache.plc4x.plugins.codegenerator.language.mspec.protocol.ProtocolHelpers;
+import org.apache.plc4x.plugins.codegenerator.language.mspec.protocol.ValidatableTypeContext;
 import org.apache.plc4x.plugins.codegenerator.protocol.Protocol;
 import org.apache.plc4x.plugins.codegenerator.protocol.TypeContext;
-import org.apache.plc4x.plugins.codegenerator.types.definitions.TypeDefinition;
 import org.apache.plc4x.plugins.codegenerator.types.exceptions.GenerationException;
 
-import java.io.InputStream;
-import java.util.Map;
-
-public class ADSProtocol implements Protocol {
+public class ADSProtocol implements Protocol, ProtocolHelpers {
 
     @Override
     public String getName() {
@@ -36,14 +34,8 @@ public class ADSProtocol implements Protocol {
 
     @Override
     public TypeContext getTypeContext() throws GenerationException {
-        InputStream schemaInputStream = ADSProtocol.class.getResourceAsStream("/protocols/ads/ads.mspec");
-        if(schemaInputStream == null) {
-            throw new GenerationException("Error loading message-format schema for protocol '" + getName() + "'");
-        }
-        TypeContext typeContext = new MessageFormatParser().parse(schemaInputStream);
-        if (typeContext.getUnresolvedTypeReferences().size() > 0) {
-            throw new GenerationException("Unresolved types left: " + typeContext.getUnresolvedTypeReferences());
-        }
+        ValidatableTypeContext typeContext = new MessageFormatParser().parse(getMspecStream());
+        typeContext.validate();
         return typeContext;
     }
 
diff --git a/protocols/bacnetip/src/main/java/org/apache/plc4x/protocol/bacnetip/BacNetIpProtocol.java b/protocols/bacnetip/src/main/java/org/apache/plc4x/protocol/bacnetip/BacNetIpProtocol.java
index 73fed799c4..99df571553 100644
--- a/protocols/bacnetip/src/main/java/org/apache/plc4x/protocol/bacnetip/BacNetIpProtocol.java
+++ b/protocols/bacnetip/src/main/java/org/apache/plc4x/protocol/bacnetip/BacNetIpProtocol.java
@@ -19,6 +19,8 @@
 package org.apache.plc4x.protocol.bacnetip;
 
 import org.apache.plc4x.plugins.codegenerator.language.mspec.parser.MessageFormatParser;
+import org.apache.plc4x.plugins.codegenerator.language.mspec.protocol.ProtocolHelpers;
+import org.apache.plc4x.plugins.codegenerator.language.mspec.protocol.ValidatableTypeContext;
 import org.apache.plc4x.plugins.codegenerator.protocol.Protocol;
 import org.apache.plc4x.plugins.codegenerator.protocol.TypeContext;
 import org.apache.plc4x.plugins.codegenerator.types.definitions.ComplexTypeDefinition;
@@ -34,7 +36,8 @@ import java.util.List;
 import java.util.Map;
 import java.util.function.Consumer;
 
-public class BacNetIpProtocol implements Protocol {
+public class BacNetIpProtocol implements Protocol, ProtocolHelpers {
+
     private static final Logger LOGGER = LoggerFactory.getLogger(BacNetIpProtocol.class);
 
     @Override
@@ -44,60 +47,23 @@ public class BacNetIpProtocol implements Protocol {
 
     @Override
     public TypeContext getTypeContext() throws GenerationException {
-        LOGGER.info("Parsing: bacnet-vendorids.mspec");
-        InputStream bacnetVendorIdsSchemaInputStream = BacNetIpProtocol.class.getResourceAsStream(
-            "/protocols/bacnetip/bacnet-vendorids.mspec");
-        if (bacnetVendorIdsSchemaInputStream == null) {
-            throw new GenerationException("Error loading vendorId schema for protocol '" + getName() + "'");
-        }
-        Map<String, TypeDefinition> typeDefinitionMap = new LinkedHashMap<>();
-        TypeContext typeContext;
+        ValidatableTypeContext typeContext;
 
-        typeContext = new MessageFormatParser().parse(bacnetVendorIdsSchemaInputStream);
-        typeDefinitionMap.putAll(typeContext.getTypeDefinitions());
+        LOGGER.info("Parsing: bacnet-vendorids.mspec");
+        typeContext = new MessageFormatParser().parse(getMspecStream("bacnet-vendorids"));
 
         LOGGER.info("Parsing: bacnet-private-enums.mspec");
-        InputStream bacnetPrivateEnumsSchemaInputStream = BacNetIpProtocol.class.getResourceAsStream(
-            "/protocols/bacnetip/bacnet-private-enums.mspec");
-        if (bacnetPrivateEnumsSchemaInputStream == null) {
-            throw new GenerationException("Error loading private enum schema for protocol '" + getName() + "'");
-        }
-        typeContext = new MessageFormatParser().parse(bacnetPrivateEnumsSchemaInputStream, typeDefinitionMap, typeContext.getUnresolvedTypeReferences());
-        typeDefinitionMap.putAll(typeContext.getTypeDefinitions());
+        typeContext = new MessageFormatParser().parse(getMspecStream("bacnet-private-enums"), typeContext);
 
         LOGGER.info("Parsing: bacnet-enums.mspec");
-        InputStream bacnetEnumsSchemaInputStream = BacNetIpProtocol.class.getResourceAsStream(
-            "/protocols/bacnetip/bacnet-enums.mspec");
-        if (bacnetEnumsSchemaInputStream == null) {
-            throw new GenerationException("Error loading enum schema for protocol '" + getName() + "'");
-        }
-        typeContext = new MessageFormatParser().parse(bacnetEnumsSchemaInputStream, typeDefinitionMap, typeContext.getUnresolvedTypeReferences());
-        typeDefinitionMap.putAll(typeContext.getTypeDefinitions());
+        typeContext = new MessageFormatParser().parse(getMspecStream("bacnet-enums"), typeContext);
 
         LOGGER.info("Parsing: bacnetip.mspec");
-        InputStream bacnetipSchemaInputStream = BacNetIpProtocol.class.getResourceAsStream(
-            "/protocols/bacnetip/bacnetip.mspec");
-        if (bacnetipSchemaInputStream == null) {
-            throw new GenerationException("Error loading schema for protocol '" + getName() + "'");
-        }
-        typeContext = new MessageFormatParser().parse(bacnetipSchemaInputStream, typeDefinitionMap, typeContext.getUnresolvedTypeReferences());
-        typeDefinitionMap.putAll(typeContext.getTypeDefinitions());
-
-        if (typeContext.getUnresolvedTypeReferences().size() > 0) {
-            throw new GenerationException("Unresolved types left: " + typeContext.getUnresolvedTypeReferences());
-        }
+        typeContext = new MessageFormatParser().parse(getMspecStream(), typeContext);
 
-        return new TypeContext() {
-            @Override
-            public Map<String, TypeDefinition> getTypeDefinitions() {
-                return typeDefinitionMap;
-            }
+        typeContext.validate();
 
-            @Override
-            public Map<String, List<Consumer<TypeDefinition>>> getUnresolvedTypeReferences() {
-                return Collections.emptyMap();
-            }
-        };
+        return typeContext;
     }
 
 }
diff --git a/protocols/c-bus/src/main/java/org/apache/plc4x/protocol/cbus/CBusProtocol.java b/protocols/c-bus/src/main/java/org/apache/plc4x/protocol/cbus/CBusProtocol.java
index 00556f1938..1f51c2d381 100644
--- a/protocols/c-bus/src/main/java/org/apache/plc4x/protocol/cbus/CBusProtocol.java
+++ b/protocols/c-bus/src/main/java/org/apache/plc4x/protocol/cbus/CBusProtocol.java
@@ -19,13 +19,15 @@
 package org.apache.plc4x.protocol.cbus;
 
 import org.apache.plc4x.plugins.codegenerator.language.mspec.parser.MessageFormatParser;
+import org.apache.plc4x.plugins.codegenerator.language.mspec.protocol.ProtocolHelpers;
+import org.apache.plc4x.plugins.codegenerator.language.mspec.protocol.ValidatableTypeContext;
 import org.apache.plc4x.plugins.codegenerator.protocol.Protocol;
 import org.apache.plc4x.plugins.codegenerator.protocol.TypeContext;
 import org.apache.plc4x.plugins.codegenerator.types.exceptions.GenerationException;
 
 import java.io.InputStream;
 
-public class CBusProtocol implements Protocol {
+public class CBusProtocol implements Protocol, ProtocolHelpers {
 
     @Override
     public String getName() {
@@ -34,14 +36,8 @@ public class CBusProtocol implements Protocol {
 
     @Override
     public TypeContext getTypeContext() throws GenerationException {
-        InputStream schemaInputStream = CBusProtocol.class.getResourceAsStream("/protocols/cbus/c-bus.mspec");
-        if(schemaInputStream == null) {
-            throw new GenerationException("Error loading message-format schema for protocol '" + getName() + "'");
-        }
-        TypeContext typeContext = new MessageFormatParser().parse(schemaInputStream);
-        if (typeContext.getUnresolvedTypeReferences().size() > 0) {
-            throw new GenerationException("Unresolved types left: " + typeContext.getUnresolvedTypeReferences());
-        }
+        ValidatableTypeContext typeContext = new MessageFormatParser().parse(getMspecStream());
+        typeContext.validate();
         return typeContext;
     }
 
diff --git a/protocols/canopen/src/main/java/org/apache/plc4x/protocol/canopen/CANOpenProtocol.java b/protocols/canopen/src/main/java/org/apache/plc4x/protocol/canopen/CANOpenProtocol.java
index 37c761131c..600ed3abfd 100644
--- a/protocols/canopen/src/main/java/org/apache/plc4x/protocol/canopen/CANOpenProtocol.java
+++ b/protocols/canopen/src/main/java/org/apache/plc4x/protocol/canopen/CANOpenProtocol.java
@@ -19,13 +19,15 @@
 package org.apache.plc4x.protocol.canopen;
 
 import org.apache.plc4x.plugins.codegenerator.language.mspec.parser.MessageFormatParser;
+import org.apache.plc4x.plugins.codegenerator.language.mspec.protocol.ProtocolHelpers;
+import org.apache.plc4x.plugins.codegenerator.language.mspec.protocol.ValidatableTypeContext;
 import org.apache.plc4x.plugins.codegenerator.protocol.Protocol;
 import org.apache.plc4x.plugins.codegenerator.protocol.TypeContext;
 import org.apache.plc4x.plugins.codegenerator.types.exceptions.GenerationException;
 
 import java.io.InputStream;
 
-public class CANOpenProtocol implements Protocol {
+public class CANOpenProtocol implements Protocol, ProtocolHelpers {
 
     @Override
     public String getName() {
@@ -34,15 +36,13 @@ public class CANOpenProtocol implements Protocol {
 
     @Override
     public TypeContext getTypeContext() throws GenerationException {
-        InputStream schemaInputStream = CANOpenProtocol.class.getResourceAsStream("/protocols/can/canopen.mspec");
-        if (schemaInputStream == null) {
-            throw new GenerationException("Error loading message-format schema for protocol '" + getName() + "'");
-        }
-        TypeContext typeContext = new MessageFormatParser().parse(schemaInputStream);
-        if (typeContext.getUnresolvedTypeReferences().size() > 0) {
-            throw new GenerationException("Unresolved types left: " + typeContext.getUnresolvedTypeReferences());
-        }
+        ValidatableTypeContext typeContext = new MessageFormatParser().parse(getMspecStream());
+        typeContext.validate();
         return typeContext;
     }
 
+    @Override
+    public String getPackageName() {
+        return "can";
+    }
 }
diff --git a/protocols/df1/src/main/java/org/apache/plc4x/protocol/df1/Df1Protocol.java b/protocols/df1/src/main/java/org/apache/plc4x/protocol/df1/Df1Protocol.java
index 1e93fc76c9..4f669466c5 100644
--- a/protocols/df1/src/main/java/org/apache/plc4x/protocol/df1/Df1Protocol.java
+++ b/protocols/df1/src/main/java/org/apache/plc4x/protocol/df1/Df1Protocol.java
@@ -19,6 +19,8 @@
 package org.apache.plc4x.protocol.df1;
 
 import org.apache.plc4x.plugins.codegenerator.language.mspec.parser.MessageFormatParser;
+import org.apache.plc4x.plugins.codegenerator.language.mspec.protocol.ProtocolHelpers;
+import org.apache.plc4x.plugins.codegenerator.language.mspec.protocol.ValidatableTypeContext;
 import org.apache.plc4x.plugins.codegenerator.protocol.Protocol;
 import org.apache.plc4x.plugins.codegenerator.protocol.TypeContext;
 import org.apache.plc4x.plugins.codegenerator.types.definitions.TypeDefinition;
@@ -27,7 +29,7 @@ import org.apache.plc4x.plugins.codegenerator.types.exceptions.GenerationExcepti
 import java.io.InputStream;
 import java.util.Map;
 
-public class Df1Protocol implements Protocol {
+public class Df1Protocol implements Protocol, ProtocolHelpers {
 
     @Override
     public String getName() {
@@ -36,14 +38,8 @@ public class Df1Protocol implements Protocol {
 
     @Override
     public TypeContext getTypeContext() throws GenerationException {
-        InputStream schemaInputStream = Df1Protocol.class.getResourceAsStream("/protocols/df1/df1.mspec");
-        if(schemaInputStream == null) {
-            throw new GenerationException("Error loading message-format schema for protocol '" + getName() + "'");
-        }
-        TypeContext typeContext = new MessageFormatParser().parse(schemaInputStream);
-        if (typeContext.getUnresolvedTypeReferences().size() > 0) {
-            throw new GenerationException("Unresolved types left: " + typeContext.getUnresolvedTypeReferences());
-        }
+        ValidatableTypeContext typeContext = new MessageFormatParser().parse(getMspecStream());
+        typeContext.validate();
         return typeContext;
     }
 
diff --git a/protocols/eip/src/main/java/org/apache/plc4x/protocol/eip/EipProtocol.java b/protocols/eip/src/main/java/org/apache/plc4x/protocol/eip/EipProtocol.java
index 81128dd6d8..9529f96b80 100644
--- a/protocols/eip/src/main/java/org/apache/plc4x/protocol/eip/EipProtocol.java
+++ b/protocols/eip/src/main/java/org/apache/plc4x/protocol/eip/EipProtocol.java
@@ -19,6 +19,8 @@
 package org.apache.plc4x.protocol.eip;
 
 import org.apache.plc4x.plugins.codegenerator.language.mspec.parser.MessageFormatParser;
+import org.apache.plc4x.plugins.codegenerator.language.mspec.protocol.ProtocolHelpers;
+import org.apache.plc4x.plugins.codegenerator.language.mspec.protocol.ValidatableTypeContext;
 import org.apache.plc4x.plugins.codegenerator.protocol.Protocol;
 import org.apache.plc4x.plugins.codegenerator.protocol.TypeContext;
 import org.apache.plc4x.plugins.codegenerator.types.definitions.TypeDefinition;
@@ -27,7 +29,7 @@ import org.apache.plc4x.plugins.codegenerator.types.exceptions.GenerationExcepti
 import java.io.InputStream;
 import java.util.Map;
 
-public class EipProtocol implements Protocol {
+public class EipProtocol implements Protocol, ProtocolHelpers {
 
     @Override
     public String getName() {
@@ -36,14 +38,8 @@ public class EipProtocol implements Protocol {
 
     @Override
     public TypeContext getTypeContext() throws GenerationException {
-        InputStream schemaInputStream = EipProtocol.class.getResourceAsStream("/protocols/eip/eip.mspec");
-        if(schemaInputStream == null) {
-            throw new GenerationException("Error loading message-format schema for protocol '" + getName() + "'");
-        }
-        TypeContext typeContext = new MessageFormatParser().parse(schemaInputStream);
-        if (typeContext.getUnresolvedTypeReferences().size() > 0) {
-            throw new GenerationException("Unresolved types left: " + typeContext.getUnresolvedTypeReferences());
-        }
+        ValidatableTypeContext typeContext = new MessageFormatParser().parse(getMspecStream());
+        typeContext.validate();
         return typeContext;
     }
 
diff --git a/protocols/firmata/src/main/java/org/apache/plc4x/protocol/firmata/FirmataProtocol.java b/protocols/firmata/src/main/java/org/apache/plc4x/protocol/firmata/FirmataProtocol.java
index 1ee546d311..ee95dfa938 100644
--- a/protocols/firmata/src/main/java/org/apache/plc4x/protocol/firmata/FirmataProtocol.java
+++ b/protocols/firmata/src/main/java/org/apache/plc4x/protocol/firmata/FirmataProtocol.java
@@ -19,6 +19,8 @@
 package org.apache.plc4x.protocol.firmata;
 
 import org.apache.plc4x.plugins.codegenerator.language.mspec.parser.MessageFormatParser;
+import org.apache.plc4x.plugins.codegenerator.language.mspec.protocol.ProtocolHelpers;
+import org.apache.plc4x.plugins.codegenerator.language.mspec.protocol.ValidatableTypeContext;
 import org.apache.plc4x.plugins.codegenerator.protocol.Protocol;
 import org.apache.plc4x.plugins.codegenerator.protocol.TypeContext;
 import org.apache.plc4x.plugins.codegenerator.types.definitions.TypeDefinition;
@@ -27,7 +29,7 @@ import org.apache.plc4x.plugins.codegenerator.types.exceptions.GenerationExcepti
 import java.io.InputStream;
 import java.util.Map;
 
-public class FirmataProtocol implements Protocol {
+public class FirmataProtocol implements Protocol, ProtocolHelpers {
 
     @Override
     public String getName() {
@@ -36,14 +38,8 @@ public class FirmataProtocol implements Protocol {
 
     @Override
     public TypeContext getTypeContext() throws GenerationException {
-        InputStream schemaInputStream = FirmataProtocol.class.getResourceAsStream("/protocols/firmata/firmata.mspec");
-        if(schemaInputStream == null) {
-            throw new GenerationException("Error loading message-format schema for protocol '" + getName() + "'");
-        }
-        TypeContext typeContext = new MessageFormatParser().parse(schemaInputStream);
-        if (typeContext.getUnresolvedTypeReferences().size() > 0) {
-            throw new GenerationException("Unresolved types left: " + typeContext.getUnresolvedTypeReferences());
-        }
+        ValidatableTypeContext typeContext = new MessageFormatParser().parse(getMspecStream());
+        typeContext.validate();
         return typeContext;
     }
 
diff --git a/protocols/genericcan/src/main/java/org/apache/plc4x/protocol/genericcan/GenericCANProtocol.java b/protocols/genericcan/src/main/java/org/apache/plc4x/protocol/genericcan/GenericCANProtocol.java
index 01f2b3d113..ef2400bc09 100644
--- a/protocols/genericcan/src/main/java/org/apache/plc4x/protocol/genericcan/GenericCANProtocol.java
+++ b/protocols/genericcan/src/main/java/org/apache/plc4x/protocol/genericcan/GenericCANProtocol.java
@@ -19,6 +19,8 @@
 package org.apache.plc4x.protocol.genericcan;
 
 import org.apache.plc4x.plugins.codegenerator.language.mspec.parser.MessageFormatParser;
+import org.apache.plc4x.plugins.codegenerator.language.mspec.protocol.ProtocolHelpers;
+import org.apache.plc4x.plugins.codegenerator.language.mspec.protocol.ValidatableTypeContext;
 import org.apache.plc4x.plugins.codegenerator.protocol.Protocol;
 import org.apache.plc4x.plugins.codegenerator.protocol.TypeContext;
 import org.apache.plc4x.plugins.codegenerator.types.definitions.TypeDefinition;
@@ -27,7 +29,7 @@ import org.apache.plc4x.plugins.codegenerator.types.exceptions.GenerationExcepti
 import java.io.InputStream;
 import java.util.Map;
 
-public class GenericCANProtocol implements Protocol {
+public class GenericCANProtocol implements Protocol, ProtocolHelpers {
 
     @Override
     public String getName() {
@@ -36,15 +38,14 @@ public class GenericCANProtocol implements Protocol {
 
     @Override
     public TypeContext getTypeContext() throws GenerationException {
-        InputStream schemaInputStream = GenericCANProtocol.class.getResourceAsStream("/protocols/can/genericcan.mspec");
-        if(schemaInputStream == null) {
-            throw new GenerationException("Error loading message-format schema for protocol '" + getName() + "'");
-        }
-        TypeContext typeContext = new MessageFormatParser().parse(schemaInputStream);
-        if (typeContext.getUnresolvedTypeReferences().size() > 0) {
-            throw new GenerationException("Unresolved types left: " + typeContext.getUnresolvedTypeReferences());
-        }
+        ValidatableTypeContext typeContext = new MessageFormatParser().parse(getMspecStream());
+        typeContext.validate();
         return typeContext;
     }
 
+
+    @Override
+    public String getPackageName() {
+        return "can";
+    }
 }
diff --git a/protocols/knxnetip/src/main/java/org/apache/plc4x/protocol/knxnetip/KnxNetIpProtocol.java b/protocols/knxnetip/src/main/java/org/apache/plc4x/protocol/knxnetip/KnxNetIpProtocol.java
index 5a6fd4644b..0676e1f1fc 100644
--- a/protocols/knxnetip/src/main/java/org/apache/plc4x/protocol/knxnetip/KnxNetIpProtocol.java
+++ b/protocols/knxnetip/src/main/java/org/apache/plc4x/protocol/knxnetip/KnxNetIpProtocol.java
@@ -19,6 +19,8 @@
 package org.apache.plc4x.protocol.knxnetip;
 
 import org.apache.plc4x.plugins.codegenerator.language.mspec.parser.MessageFormatParser;
+import org.apache.plc4x.plugins.codegenerator.language.mspec.protocol.ProtocolHelpers;
+import org.apache.plc4x.plugins.codegenerator.language.mspec.protocol.ValidatableTypeContext;
 import org.apache.plc4x.plugins.codegenerator.protocol.Protocol;
 import org.apache.plc4x.plugins.codegenerator.protocol.TypeContext;
 import org.apache.plc4x.plugins.codegenerator.types.definitions.TypeDefinition;
@@ -33,7 +35,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.function.Consumer;
 
-public class KnxNetIpProtocol implements Protocol {
+public class KnxNetIpProtocol implements Protocol, ProtocolHelpers {
 
     private static final Logger LOGGER = LoggerFactory.getLogger(KnxNetIpProtocol.class);
 
@@ -44,51 +46,19 @@ public class KnxNetIpProtocol implements Protocol {
 
     @Override
     public TypeContext getTypeContext() throws GenerationException {
-        LOGGER.info("Parsing: knxnetip.mspec");
-        InputStream schemaInputStream = KnxNetIpProtocol.class.getResourceAsStream(
-            "/protocols/knxnetip/knxnetip.mspec");
-        if (schemaInputStream == null) {
-            throw new GenerationException("Error loading message-format schema for protocol '" + getName() + "'");
-        }
-        Map<String, TypeDefinition> typeDefinitionMap = new LinkedHashMap<>();
-        TypeContext typeContext;
+        ValidatableTypeContext typeContext;
 
-        typeContext = new MessageFormatParser().parse(schemaInputStream);
-        typeDefinitionMap.putAll(typeContext.getTypeDefinitions());
+        LOGGER.info("Parsing: knxnetip.mspec");
+        typeContext = new MessageFormatParser().parse(getMspecStream());
 
         LOGGER.info("Parsing: knx-master-data.mspec");
-        InputStream masterDataInputStream = KnxNetIpProtocol.class.getResourceAsStream(
-            "/protocols/knxnetip/knx-master-data.mspec");
-        if (masterDataInputStream == null) {
-            throw new GenerationException("Error loading knx-master-data schema for protocol '" + getName() + "'");
-        }
-        typeContext = new MessageFormatParser().parse(masterDataInputStream, typeContext.getUnresolvedTypeReferences());
-        typeDefinitionMap.putAll(typeContext.getTypeDefinitions());
+        typeContext = new MessageFormatParser().parse(getMspecStream("knx-master-data"), typeContext);
 
         LOGGER.info("Parsing: device-info.mspec");
-        InputStream deviceDataInputStream = KnxNetIpProtocol.class.getResourceAsStream(
-            "/protocols/knxnetip/device-info.mspec");
-        if (deviceDataInputStream == null) {
-            throw new GenerationException("Error loading device-info schema for protocol '" + getName() + "'");
-        }
-        typeContext = new MessageFormatParser().parse(deviceDataInputStream, typeContext.getUnresolvedTypeReferences());
-        typeDefinitionMap.putAll(typeContext.getTypeDefinitions());
-
-        if (typeContext.getUnresolvedTypeReferences().size() > 0) {
-            throw new GenerationException("Unresolved types left: " + typeContext.getUnresolvedTypeReferences());
-        }
-
-        return new TypeContext() {
-            @Override
-            public Map<String, TypeDefinition> getTypeDefinitions() {
-                return typeDefinitionMap;
-            }
+        typeContext = new MessageFormatParser().parse(getMspecStream("device-info"), typeContext);
 
-            @Override
-            public Map<String, List<Consumer<TypeDefinition>>> getUnresolvedTypeReferences() {
-                return Collections.emptyMap();
-            }
-        };
+        typeContext.validate();
+        return typeContext;
     }
 
 }
diff --git a/protocols/modbus/src/main/java/org/apache/plc4x/protocol/modbus/ModbusProtocol.java b/protocols/modbus/src/main/java/org/apache/plc4x/protocol/modbus/ModbusProtocol.java
index 965a50df3b..7b7c6d118b 100644
--- a/protocols/modbus/src/main/java/org/apache/plc4x/protocol/modbus/ModbusProtocol.java
+++ b/protocols/modbus/src/main/java/org/apache/plc4x/protocol/modbus/ModbusProtocol.java
@@ -19,6 +19,8 @@
 package org.apache.plc4x.protocol.modbus;
 
 import org.apache.plc4x.plugins.codegenerator.language.mspec.parser.MessageFormatParser;
+import org.apache.plc4x.plugins.codegenerator.language.mspec.protocol.ProtocolHelpers;
+import org.apache.plc4x.plugins.codegenerator.language.mspec.protocol.ValidatableTypeContext;
 import org.apache.plc4x.plugins.codegenerator.protocol.Protocol;
 import org.apache.plc4x.plugins.codegenerator.protocol.TypeContext;
 import org.apache.plc4x.plugins.codegenerator.types.definitions.TypeDefinition;
@@ -27,7 +29,7 @@ import org.apache.plc4x.plugins.codegenerator.types.exceptions.GenerationExcepti
 import java.io.InputStream;
 import java.util.Map;
 
-public class ModbusProtocol implements Protocol {
+public class ModbusProtocol implements Protocol, ProtocolHelpers {
 
     @Override
     public String getName() {
@@ -36,14 +38,8 @@ public class ModbusProtocol implements Protocol {
 
     @Override
     public TypeContext getTypeContext() throws GenerationException {
-        InputStream schemaInputStream = ModbusProtocol.class.getResourceAsStream("/protocols/modbus/modbus.mspec");
-        if(schemaInputStream == null) {
-            throw new GenerationException("Error loading message-format schema for protocol '" + getName() + "'");
-        }
-        TypeContext typeContext = new MessageFormatParser().parse(schemaInputStream);
-        if (typeContext.getUnresolvedTypeReferences().size() > 0) {
-            throw new GenerationException("Unresolved types left: " + typeContext.getUnresolvedTypeReferences());
-        }
+        ValidatableTypeContext typeContext = new MessageFormatParser().parse(getMspecStream());
+        typeContext.validate();
         return typeContext;
     }
 
diff --git a/protocols/opcua/src/main/java/org/apache/plc4x/protocol/opcua/OpcuaProtocol.java b/protocols/opcua/src/main/java/org/apache/plc4x/protocol/opcua/OpcuaProtocol.java
index 938ed07a9f..e580c64030 100644
--- a/protocols/opcua/src/main/java/org/apache/plc4x/protocol/opcua/OpcuaProtocol.java
+++ b/protocols/opcua/src/main/java/org/apache/plc4x/protocol/opcua/OpcuaProtocol.java
@@ -19,19 +19,17 @@
 package org.apache.plc4x.protocol.opcua;
 
 import org.apache.plc4x.plugins.codegenerator.language.mspec.parser.MessageFormatParser;
+import org.apache.plc4x.plugins.codegenerator.language.mspec.protocol.ProtocolHelpers;
+import org.apache.plc4x.plugins.codegenerator.language.mspec.protocol.ValidatableTypeContext;
 import org.apache.plc4x.plugins.codegenerator.protocol.Protocol;
 import org.apache.plc4x.plugins.codegenerator.protocol.TypeContext;
-import org.apache.plc4x.plugins.codegenerator.types.definitions.TypeDefinition;
 import org.apache.plc4x.plugins.codegenerator.types.exceptions.GenerationException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
-import java.io.InputStream;
-import java.util.Collections;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.function.Consumer;
+public class OpcuaProtocol implements Protocol, ProtocolHelpers {
 
-public class OpcuaProtocol implements Protocol {
+    private static final Logger LOGGER = LoggerFactory.getLogger(OpcuaProtocol.class);
 
     @Override
     public String getName() {
@@ -40,59 +38,22 @@ public class OpcuaProtocol implements Protocol {
 
     @Override
     public TypeContext getTypeContext() throws GenerationException {
-        System.out.println("Parsing: opc-manual.mspec");
-        InputStream manualInputStream = OpcuaProtocol.class.getResourceAsStream(
-            "/protocols/opcua/opc-manual.mspec");
-        if (manualInputStream == null) {
-            throw new GenerationException("Error loading message-format schema for protocol '" + getName() + "'");
-        }
-        Map<String, TypeDefinition> typeDefinitionMap = new LinkedHashMap<>();
-        TypeContext typeContext;
+        LOGGER.info("Parsing: opc-manual.mspec");
+        ValidatableTypeContext typeContext;
 
-        typeContext = new MessageFormatParser().parse(manualInputStream);
-        typeDefinitionMap.putAll(typeContext.getTypeDefinitions());
+        typeContext = new MessageFormatParser().parse(getMspecStream("opc-manual"));
 
-        System.out.println("Parsing: opc-services.mspec");
-        InputStream servicesInputStream = OpcuaProtocol.class.getResourceAsStream(
-            "/protocols/opcua/opc-services.mspec");
-        if (servicesInputStream == null) {
-            throw new GenerationException("Error loading message-format schema for protocol '" + getName() + "'");
-        }
-        typeContext = new MessageFormatParser().parse(servicesInputStream, typeContext.getUnresolvedTypeReferences());
-        typeDefinitionMap.putAll(typeContext.getTypeDefinitions());
+        LOGGER.info("Parsing: opc-services.mspec");
+        typeContext = new MessageFormatParser().parse(getMspecStream("opc-services"), typeContext);
 
-        System.out.println("Parsing: opc-status.mspec");
-        InputStream statusInputStream = OpcuaProtocol.class.getResourceAsStream(
-            "/protocols/opcua/opc-status.mspec");
-        if (statusInputStream == null) {
-            throw new GenerationException("Error loading message-format schema for protocol '" + getName() + "'");
-        }
-        typeContext = new MessageFormatParser().parse(statusInputStream, typeContext.getUnresolvedTypeReferences());
-        typeDefinitionMap.putAll(typeContext.getTypeDefinitions());
+        LOGGER.info("Parsing: opc-status.mspec");
+        typeContext = new MessageFormatParser().parse(getMspecStream("opc-status"), typeContext);
 
-        System.out.println("Parsing: opc-types.mspec");
-        InputStream typesInputStream = OpcuaProtocol.class.getResourceAsStream(
-            "/protocols/opcua/opc-types.mspec");
-        if (typesInputStream == null) {
-            throw new GenerationException("Error loading message-format schema for protocol '" + getName() + "'");
-        }
-        typeContext = new MessageFormatParser().parse(typesInputStream, typeContext.getUnresolvedTypeReferences());
-        typeDefinitionMap.putAll(typeContext.getTypeDefinitions());
+        LOGGER.info("Parsing: opc-types.mspec");
+        typeContext = new MessageFormatParser().parse(getMspecStream("opc-types"), typeContext);
 
-        if (typeContext.getUnresolvedTypeReferences().size() > 0) {
-            throw new GenerationException("Unresolved types left: " + typeContext.getUnresolvedTypeReferences());
-        }
+        typeContext.validate();
 
-        return new TypeContext() {
-            @Override
-            public Map<String, TypeDefinition> getTypeDefinitions() {
-                return typeDefinitionMap;
-            }
-
-            @Override
-            public Map<String, List<Consumer<TypeDefinition>>> getUnresolvedTypeReferences() {
-                return Collections.emptyMap();
-            }
-        };
+        return typeContext;
     }
 }
diff --git a/protocols/plc4x/src/main/java/org/apache/plc4x/protocol/plc4x/v0/Plc4xProtocol.java b/protocols/plc4x/src/main/java/org/apache/plc4x/protocol/plc4x/v0/Plc4xProtocol.java
index 050109d4ac..e28341ce2c 100644
--- a/protocols/plc4x/src/main/java/org/apache/plc4x/protocol/plc4x/v0/Plc4xProtocol.java
+++ b/protocols/plc4x/src/main/java/org/apache/plc4x/protocol/plc4x/v0/Plc4xProtocol.java
@@ -19,16 +19,15 @@
 package org.apache.plc4x.protocol.plc4x.v0;
 
 import org.apache.plc4x.plugins.codegenerator.language.mspec.parser.MessageFormatParser;
+import org.apache.plc4x.plugins.codegenerator.language.mspec.protocol.ProtocolHelpers;
+import org.apache.plc4x.plugins.codegenerator.language.mspec.protocol.ValidatableTypeContext;
 import org.apache.plc4x.plugins.codegenerator.protocol.Protocol;
 import org.apache.plc4x.plugins.codegenerator.protocol.TypeContext;
-import org.apache.plc4x.plugins.codegenerator.types.definitions.TypeDefinition;
 import org.apache.plc4x.plugins.codegenerator.types.exceptions.GenerationException;
 
-import java.io.InputStream;
-import java.util.Map;
 import java.util.Optional;
 
-public class Plc4xProtocol implements Protocol {
+public class Plc4xProtocol implements Protocol, ProtocolHelpers {
 
     @Override
     public String getName() {
@@ -42,14 +41,8 @@ public class Plc4xProtocol implements Protocol {
 
     @Override
     public TypeContext getTypeContext() throws GenerationException {
-        InputStream schemaInputStream = Plc4xProtocol.class.getResourceAsStream("/protocols/plc4x/v0/plc4x.mspec");
-        if(schemaInputStream == null) {
-            throw new GenerationException("Error loading message-format schema for protocol '" + getName() + "'");
-        }
-        TypeContext typeContext = new MessageFormatParser().parse(schemaInputStream);
-        if (typeContext.getUnresolvedTypeReferences().size() > 0) {
-            throw new GenerationException("Unresolved types left: " + typeContext.getUnresolvedTypeReferences());
-        }
+        ValidatableTypeContext typeContext = new MessageFormatParser().parse(getMspecStream());
+        typeContext.validate();
         return typeContext;
     }
 
diff --git a/protocols/profinet/src/main/java/org/apache/plc4x/protocol/profinet/ProfinetProtocol.java b/protocols/profinet/src/main/java/org/apache/plc4x/protocol/profinet/ProfinetProtocol.java
index 3c6ee080c4..b5a1d60ef1 100644
--- a/protocols/profinet/src/main/java/org/apache/plc4x/protocol/profinet/ProfinetProtocol.java
+++ b/protocols/profinet/src/main/java/org/apache/plc4x/protocol/profinet/ProfinetProtocol.java
@@ -19,15 +19,13 @@
 package org.apache.plc4x.protocol.profinet;
 
 import org.apache.plc4x.plugins.codegenerator.language.mspec.parser.MessageFormatParser;
+import org.apache.plc4x.plugins.codegenerator.language.mspec.protocol.ProtocolHelpers;
+import org.apache.plc4x.plugins.codegenerator.language.mspec.protocol.ValidatableTypeContext;
 import org.apache.plc4x.plugins.codegenerator.protocol.Protocol;
 import org.apache.plc4x.plugins.codegenerator.protocol.TypeContext;
-import org.apache.plc4x.plugins.codegenerator.types.definitions.TypeDefinition;
 import org.apache.plc4x.plugins.codegenerator.types.exceptions.GenerationException;
 
-import java.io.InputStream;
-import java.util.Map;
-
-public class ProfinetProtocol implements Protocol {
+public class ProfinetProtocol implements Protocol, ProtocolHelpers {
 
     @Override
     public String getName() {
@@ -36,14 +34,8 @@ public class ProfinetProtocol implements Protocol {
 
     @Override
     public TypeContext getTypeContext() throws GenerationException {
-        InputStream schemaInputStream = ProfinetProtocol.class.getResourceAsStream("/protocols/profinet/profinet.mspec");
-        if(schemaInputStream == null) {
-            throw new GenerationException("Error loading message-format schema for protocol '" + getName() + "'");
-        }
-        TypeContext typeContext = new MessageFormatParser().parse(schemaInputStream);
-        if (typeContext.getUnresolvedTypeReferences().size() > 0) {
-            throw new GenerationException("Unresolved types left: " + typeContext.getUnresolvedTypeReferences());
-        }
+        ValidatableTypeContext typeContext = new MessageFormatParser().parse(getMspecStream());
+        typeContext.validate();
         return typeContext;
     }
 
diff --git a/protocols/s7/src/main/java/org/apache/plc4x/protocol/s7/S7Protocol.java b/protocols/s7/src/main/java/org/apache/plc4x/protocol/s7/S7Protocol.java
index df7c36ac49..b1a1e9f497 100644
--- a/protocols/s7/src/main/java/org/apache/plc4x/protocol/s7/S7Protocol.java
+++ b/protocols/s7/src/main/java/org/apache/plc4x/protocol/s7/S7Protocol.java
@@ -19,6 +19,8 @@
 package org.apache.plc4x.protocol.s7;
 
 import org.apache.plc4x.plugins.codegenerator.language.mspec.parser.MessageFormatParser;
+import org.apache.plc4x.plugins.codegenerator.language.mspec.protocol.ProtocolHelpers;
+import org.apache.plc4x.plugins.codegenerator.language.mspec.protocol.ValidatableTypeContext;
 import org.apache.plc4x.plugins.codegenerator.protocol.Protocol;
 import org.apache.plc4x.plugins.codegenerator.protocol.TypeContext;
 import org.apache.plc4x.plugins.codegenerator.types.definitions.TypeDefinition;
@@ -27,7 +29,7 @@ import org.apache.plc4x.plugins.codegenerator.types.exceptions.GenerationExcepti
 import java.io.InputStream;
 import java.util.Map;
 
-public class S7Protocol implements Protocol {
+public class S7Protocol implements Protocol, ProtocolHelpers {
 
     @Override
     public String getName() {
@@ -36,14 +38,8 @@ public class S7Protocol implements Protocol {
 
     @Override
     public TypeContext getTypeContext() throws GenerationException {
-        InputStream schemaInputStream = S7Protocol.class.getResourceAsStream("/protocols/s7/s7.mspec");
-        if(schemaInputStream == null) {
-            throw new GenerationException("Error loading message-format schema for protocol '" + getName() + "'");
-        }
-        TypeContext typeContext = new MessageFormatParser().parse(schemaInputStream);
-        if (typeContext.getUnresolvedTypeReferences().size() > 0) {
-            throw new GenerationException("Unresolved types left: " + typeContext.getUnresolvedTypeReferences());
-        }
+        ValidatableTypeContext typeContext = new MessageFormatParser().parse(getMspecStream());
+        typeContext.validate();
         return typeContext;
     }
 
diff --git a/protocols/simulated/src/main/java/org/apache/plc4x/protocol/simulated/SimulatedProtocol.java b/protocols/simulated/src/main/java/org/apache/plc4x/protocol/simulated/SimulatedProtocol.java
index 6f3dcd10b3..c99b5ff00d 100644
--- a/protocols/simulated/src/main/java/org/apache/plc4x/protocol/simulated/SimulatedProtocol.java
+++ b/protocols/simulated/src/main/java/org/apache/plc4x/protocol/simulated/SimulatedProtocol.java
@@ -19,15 +19,13 @@
 package org.apache.plc4x.protocol.simulated;
 
 import org.apache.plc4x.plugins.codegenerator.language.mspec.parser.MessageFormatParser;
+import org.apache.plc4x.plugins.codegenerator.language.mspec.protocol.ProtocolHelpers;
+import org.apache.plc4x.plugins.codegenerator.language.mspec.protocol.ValidatableTypeContext;
 import org.apache.plc4x.plugins.codegenerator.protocol.Protocol;
 import org.apache.plc4x.plugins.codegenerator.protocol.TypeContext;
-import org.apache.plc4x.plugins.codegenerator.types.definitions.TypeDefinition;
 import org.apache.plc4x.plugins.codegenerator.types.exceptions.GenerationException;
 
-import java.io.InputStream;
-import java.util.Map;
-
-public class SimulatedProtocol implements Protocol {
+public class SimulatedProtocol implements Protocol, ProtocolHelpers {
 
     @Override
     public String getName() {
@@ -36,14 +34,8 @@ public class SimulatedProtocol implements Protocol {
 
     @Override
     public TypeContext getTypeContext() throws GenerationException {
-        InputStream schemaInputStream = SimulatedProtocol.class.getResourceAsStream("/protocols/simulated/simulated.mspec");
-        if(schemaInputStream == null) {
-            throw new GenerationException("Error loading message-format schema for protocol '" + getName() + "'");
-        }
-        TypeContext typeContext = new MessageFormatParser().parse(schemaInputStream);
-        if (typeContext.getUnresolvedTypeReferences().size() > 0) {
-            throw new GenerationException("Unresolved types left: " + typeContext.getUnresolvedTypeReferences());
-        }
+        ValidatableTypeContext typeContext = new MessageFormatParser().parse(getMspecStream());
+        typeContext.validate();
         return typeContext;
     }
 
diff --git a/protocols/socketcan/src/main/java/org/apache/plc4x/protocol/socketcan/SocketCANProtocol.java b/protocols/socketcan/src/main/java/org/apache/plc4x/protocol/socketcan/SocketCANProtocol.java
index 8604e2b740..3e3727d05a 100644
--- a/protocols/socketcan/src/main/java/org/apache/plc4x/protocol/socketcan/SocketCANProtocol.java
+++ b/protocols/socketcan/src/main/java/org/apache/plc4x/protocol/socketcan/SocketCANProtocol.java
@@ -20,6 +20,8 @@
 package org.apache.plc4x.protocol.socketcan;
 
 import org.apache.plc4x.plugins.codegenerator.language.mspec.parser.MessageFormatParser;
+import org.apache.plc4x.plugins.codegenerator.language.mspec.protocol.ProtocolHelpers;
+import org.apache.plc4x.plugins.codegenerator.language.mspec.protocol.ValidatableTypeContext;
 import org.apache.plc4x.plugins.codegenerator.protocol.Protocol;
 import org.apache.plc4x.plugins.codegenerator.protocol.TypeContext;
 import org.apache.plc4x.plugins.codegenerator.types.definitions.TypeDefinition;
@@ -28,7 +30,7 @@ import org.apache.plc4x.plugins.codegenerator.types.exceptions.GenerationExcepti
 import java.io.InputStream;
 import java.util.Map;
 
-public class SocketCANProtocol implements Protocol {
+public class SocketCANProtocol implements Protocol, ProtocolHelpers {
 
     @Override
     public String getName() {
@@ -37,15 +39,13 @@ public class SocketCANProtocol implements Protocol {
 
     @Override
     public TypeContext getTypeContext() throws GenerationException {
-        InputStream schemaInputStream = SocketCANProtocol.class.getResourceAsStream("/protocols/can/socketcan.mspec");
-        if(schemaInputStream == null) {
-            throw new GenerationException("Error loading message-format schema for protocol '" + getName() + "'");
-        }
-        TypeContext typeContext = new MessageFormatParser().parse(schemaInputStream);
-        if (typeContext.getUnresolvedTypeReferences().size() > 0) {
-            throw new GenerationException("Unresolved types left: " + typeContext.getUnresolvedTypeReferences());
-        }
+        ValidatableTypeContext typeContext = new MessageFormatParser().parse(getMspecStream());
+        typeContext.validate();
         return typeContext;
     }
 
+    @Override
+    public String getPackageName() {
+        return "can";
+    }
 }