public WrappedDataType findDataType(String datatype) throws CancelledException {

		// NOTE: previous case-insensitive search was removed since type names
		// should be case-sensitive
		datatype = datatype.trim();

		if (datatype == null || datatype.length() == 0) {
			return null;
		}

		if (NO_TYPE.equals(datatype)) {
			return NO_TYPE_DATATYPE;
		}

		String dataTypeName = datatype;

		// Handle potential malformed datatypes where some datatype is implied
		// *
		// **
		// [16]
		if (dataTypeName.startsWith("*") || dataTypeName.startsWith("[")) {
			// prepend undefined since intent is some datatype
			Msg.warn(this, "dataTypeName \"" + dataTypeName +
				"\" references a pointer or array without a declared datatype; assuming undefined");
			dataTypeName = "undefined" + dataTypeName;
		}

		// Deal with other unrecognized types
		dataTypeName = StringUtils.replace(dataTypeName, "<NoType>", "undefined");

		// Example type representations:
		// char *[2][3]     pointer(array(array(char,3),2))
		// char *[2][3] *   pointer(array(array(pointer(char),3),2))
		// char  [0][2][3] *  array(array(array(pointer(char),3),2),0)
		// char  [2][3] *    array(array(pointer(char),3),2)

		int basePointerDepth = 0;
		while (dataTypeName.endsWith("*")) {
			++basePointerDepth;
			dataTypeName = dataTypeName.substring(0, dataTypeName.length() - 1).trim();
		}

		boolean isZeroLengthArray = false;
		List<Integer> arrayDimensions = null;
		if (dataTypeName.endsWith("]")) {
			arrayDimensions = new ArrayList<>();
			dataTypeName = parseArrayDimensions(dataTypeName, arrayDimensions);
			if (dataTypeName == null) {
				Msg.error(this, "Failed to parse array dimensions: " + datatype);
				return null;
			}
			isZeroLengthArray = (arrayDimensions.get(arrayDimensions.size() - 1) == 0);
		}

		int pointerDepth = 0;
		if (arrayDimensions != null) {
			while (dataTypeName.endsWith("*")) {
				++pointerDepth;
				dataTypeName = dataTypeName.substring(0, dataTypeName.length() - 1).trim();
			}
			if (pointerDepth != 0 && isZeroLengthArray) {
				Msg.error(this, "Unsupported pointer to zero-length array: " + datatype);
				return null;
			}
		}

		// Find base data-type (name may include namespace, e.g., Foo::MyType)
		// Primary cache (dataTypeCache) may contain namespace qualified data type

		DataType dt = findBaseDataType(dataTypeName, monitor);
		if (dt == null) {
			return null; // base type not found
		}

		while (basePointerDepth-- != 0) {
			dt = createPointer(dt);
		}

		if (arrayDimensions != null) {
			dt = createArray(dt, arrayDimensions);
		}

		while (pointerDepth-- != 0) {
			dt = createPointer(dt);
		}

		return new WrappedDataType(dt, isZeroLengthArray, false);
	}
Find a data-type by name in a case-sensitive manner.
@param monitor task monitor
@param dataTypeName data-type name (may be qualified by its namespace)
@return wrapped data-type or null if not found.
@throws CancelledException if operation is cancelled
Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/PdbDataTypeParser.java