private void read(XmlPullParser parser, ResourceFile parentDirectory, String ldefs) {
		LanguageID id;
		Endian endian;
		Endian instructionEndian;
		int size;
		String variant;
		String processorName;
		int version;
		int minorVersion;
		String descriptionText;
		boolean deprecated;
		String slafilename;
		String pspec;
		String manualindexfile;
		ArrayList<CompilerSpecDescription> compilerSpecs;

		XmlElement start = parser.start("language_definitions");
		XmlElement languageEnter;
		while ((languageEnter = parser.softStart("language")) != null) {

			boolean hidden = SpecXmlUtils.decodeBoolean(languageEnter.getAttribute("hidden"));
			if (hidden && !SystemUtilities.isInDevelopmentMode()) {
				parser.discardSubTree(languageEnter);
				continue;
			}

			id = new LanguageID(languageEnter.getAttribute("id"));
			processorName = languageEnter.getAttribute("processor");
			endian = Endian.valueOf(languageEnter.getAttribute("endian").toUpperCase());
			instructionEndian = endian;
			if (languageEnter.hasAttribute("instructionEndian")) {
				instructionEndian =
					Endian.valueOf(languageEnter.getAttribute("instructionEndian").toUpperCase());
			}
			size = SpecXmlUtils.decodeInt(languageEnter.getAttribute("size"));
			variant = languageEnter.getAttribute("variant");
			String text = languageEnter.getAttribute("version");
			String[] versionPieces = text.split("\\.");
			version = 1;
			minorVersion = 0;
			try {
				version = SpecXmlUtils.decodeInt(versionPieces[0]);
				if (versionPieces.length > 1) {
					minorVersion = SpecXmlUtils.decodeInt(versionPieces[1]);
				}
			}
			catch (Exception e) {
				throw new SleighException(
					"Version tag must specify address <major>[.<minor>] version numbers", e);
			}
			deprecated = SpecXmlUtils.decodeBoolean(languageEnter.getAttribute("deprecated"));
			slafilename = languageEnter.getAttribute("slafile");
			manualindexfile = languageEnter.getAttribute("manualindexfile");
			pspec = languageEnter.getAttribute("processorspec");

			compilerSpecs = new ArrayList<CompilerSpecDescription>();

			while (!parser.peek().getName().equals("description")) {
				parser.discardSubTree();
			}
			XmlElement descriptionStart = parser.start();

			XmlElement descriptionEnd = parser.end(descriptionStart);
			descriptionText = descriptionEnd.getText();

			Map<String, Integer> truncatedSpaceMap = null;

			Map<String, List<String>> externalNameMap = new HashMap<String, List<String>>();

			XmlElement compiler;
			XmlElement element;
			SleighLanguageDescription description;
			XmlElement externalName;

			while ((element = parser.softStart("truncate_space")) != null) {
				String spaceName = element.getAttribute("space");
				int truncatedSize = SpecXmlUtils.decodeInt(element.getAttribute("size"));
				if (truncatedSpaceMap == null) {
					truncatedSpaceMap = new HashMap<String, Integer>();
				}
				if (truncatedSpaceMap.put(spaceName, truncatedSize) != null) {
					throw new SleighException(
						"truncated space '" + spaceName + "' alread specified");
				}
				parser.end(element);
			}
			while ((compiler = parser.softStart("compiler")) != null) {
				String compilerID = compiler.getAttribute("id");
				final CompilerSpecID compilerSpecID = new CompilerSpecID(compilerID);
				final String compilerSpecName = compiler.getAttribute("name");
				final String compilerSpecFilename = compiler.getAttribute("spec");
				final ResourceFile compilerSpecFile =
					findFile(parentDirectory, compilerSpecFilename, ".cspec");
				FileResolutionResult result = existsAndIsCaseDependent(compilerSpecFile);
				if (!result.isOk()) {
					throw new SleighException("cspec file " + compilerSpecFile +
						" is not properly case dependent: " + result.getMessage());
				}
				final SleighCompilerSpecDescription sleighCompilerSpecDescription =
					new SleighCompilerSpecDescription(compilerSpecID, compilerSpecName,
						compilerSpecFile);
				compilerSpecs.add(sleighCompilerSpecDescription);
				parser.end(compiler);
			}
			while ((externalName = parser.softStart("external_name")) != null) {
				String tool = externalName.getAttribute("tool");
				String name = externalName.getAttribute("name");

				if (tool != null && name != null && tool.length() > 0 && name.length() > 0) {
					List<String> nameList = externalNameMap.get(tool);
					if (nameList == null) {
						nameList = new ArrayList<String>();
						externalNameMap.put(tool, nameList);
					}
					nameList.add(name);
				}
				parser.end(externalName);
			}

			// skip any deprecated-specl-tags, or anything else for that matter
			while (!parser.peek().isEnd()) {
				parser.discardSubTree();
			}

			// skip the language end tag
			parser.end(languageEnter);
			description = new SleighLanguageDescription(id, descriptionText,
				Processor.findOrPossiblyCreateProcessor(processorName), endian, instructionEndian,
				size, variant, version, minorVersion, deprecated, truncatedSpaceMap, compilerSpecs,
				externalNameMap);
			final ResourceFile defsFile = new ResourceFile(parentDirectory, ldefs);
			FileResolutionResult result = existsAndIsCaseDependent(defsFile);
			if (!result.isOk()) {
				throw new SleighException("ldefs file " + defsFile +
					" is not properly case dependent: " + result.getMessage());
			}
			description.setDefsFile(defsFile);
			final ResourceFile specFile = findFile(parentDirectory, pspec, ".pspec");
			result = existsAndIsCaseDependent(specFile);
			if (!result.isOk()) {
				throw new SleighException("pspec file " + specFile +
					" is not properly case dependent: " + result.getMessage());
			}
			description.setSpecFile(specFile);

			ResourceFile slaFile;
			try {
				slaFile = findFile(parentDirectory, slafilename, ".slaspec");
				result = existsAndIsCaseDependent(slaFile);
				if (!result.isOk()) {
					throw new SleighException("sla file " + slaFile +
						" is not properly case dependent: " + result.getMessage());
				}
				description.setSlaFile(slaFile);
			}
			catch (SleighException e) {
				int index = slafilename.lastIndexOf('.');
				String slabase = slafilename.substring(0, index);

				String slaspecfilename = slabase + ".slaspec";

				ResourceFile slaspecFile = findFile(parentDirectory, slaspecfilename, ".slaspec");
				result = existsAndIsCaseDependent(slaspecFile);
				if (!result.isOk()) {
					throw new SleighException("sla file source " + slaspecFile +
						" is not properly case dependent: " + result.getMessage());
				}

				slaFile = new ResourceFile(slaspecFile.getParentFile(), slafilename);
				description.setSlaFile(slaFile);
			}

			try {
				if (manualindexfile != null) {
					ResourceFile manualIndexFile =
						findFile(parentDirectory, manualindexfile, ".idx");
					result = existsAndIsCaseDependent(manualIndexFile);
					if (result.isOk()) {
						description.setManualIndexFile(manualIndexFile);
					}
					else {
						throw new SleighException(result.getMessage());
					}
				}
			}
			catch (SleighException ex) { // Error with the manual shouldn't prevent language from loading
				Msg.error(this, ex.getMessage());
			}
			if (descriptions.put(id, description) != null) {
				Msg.showError(this, null, "Duplicate Sleigh Language ID",
					"Language " + id + " previously defined: " + defsFile);
			}
		}
		parser.end(start);
	}
Construct language provider (intended for test use only)
@param ldefsFile language definitions file
@throws SAXException if parse error occurs
@throws IOException if IO error occurs
Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/processors/sleigh/SleighLanguageProvider.java