public MessageLog read(Program program, TaskMonitor monitor, XmlProgramOptions options)
			throws SAXException, IOException, AddressFormatException {

		if (getProgramInfo() == null) {
			throw new SAXException("Unsupported XML Format!");
		}

		if (!options.isAddToProgram()) {
			Memory memory = program.getMemory();
			MemoryBlock[] blocks = memory.getBlocks();
			for (MemoryBlock block : blocks) {
				try {
					memory.removeBlock(block, monitor);
				}
				catch (LockException e) {
					throw new RuntimeException(e);//should never happen...
				}
			}
			Listing listing = program.getListing();
			String[] treeNames = listing.getTreeNames();
			for (String treeName : treeNames) {
				listing.removeTree(treeName);
			}
		}

		//check to see if this happens to be a file
		//that was created with a bleeding-edge build
		//of 2.1 before it's official release.

		if (isOldXml()) {
			MessageLog log = new MessageLog();
			log.appendMsg("File is old 2.0 XML which is no longer supported!");
			return log;
		}

		XmlMessageLog log = new XmlMessageLog();
		MyErrorHandler errHandler = new MyErrorHandler(log);
		XmlPullParser parser = XmlPullParserFactory.create(file, errHandler, false);
		log.setParser(parser);

		if (!options.isAddToProgram()) {
			program.setExecutableFormat(getStandardName(info.exeFormat));
			program.setExecutablePath(info.exePath);

			try {
				SimpleDateFormat format = new SimpleDateFormat("EEE MMM dd HH:mm:ss ZZZ yyyy");
				Date creationDate = format.parse(info.timestamp);
				Options pl = program.getOptions(Program.PROGRAM_INFO);
				pl.setDate(Program.DATE_CREATED, creationDate);
			}
			catch (Exception e) {
				Options pl = program.getOptions(Program.PROGRAM_INFO);
				pl.setDate(Program.DATE_CREATED, Program.JANUARY_1_1970);
			}
		}

		boolean secondPassRequired = false;
		final XmlElement programStart = parser.start("PROGRAM");
		try {
			while (parser.hasNext() && parser.peek().isStart() && !monitor.isCancelled()) {
				XmlElement element = parser.peek();
				String name = element.getName();

				if (options.isData() && name.equals("DATATYPES")) {
					monitor.setMessage("Processing DATA TYPES ...");
					DataTypesXmlMgr mgr =
						new DataTypesXmlMgr(program.getListing().getDataTypeManager(), log);
					mgr.read(parser, monitor);
				}
				else if (options.isMemoryBlocks() && name.equals("MEMORY_MAP")) {
					monitor.setMessage("Processing MEMORY MAP ...");
					MemoryMapXmlMgr mgr = new MemoryMapXmlMgr(program, log);
					mgr.read(parser, options.isOverwriteMemoryConflicts(), monitor,
						file.getParent());
				}
				else if (options.isRegisters() && name.equals("REGISTER_VALUES")) {
					monitor.setMessage("Processing REGISTER VALUES ...");
					RegisterValuesXmlMgr mgr = new RegisterValuesXmlMgr(program, log);
					mgr.read(parser, monitor);
				}
				else if (options.isInstructions() && name.equals("CODE")) {
					monitor.setMessage("Processing CODE ...");
					CodeXmlMgr mgr = new CodeXmlMgr(program, log);
					mgr.read(parser, monitor);
				}
				else if (options.isData() && name.equals("DATA")) {
					monitor.setMessage("Processing DATA ...");
					DefinedDataXmlMgr mgr = new DefinedDataXmlMgr(program, log);
					mgr.read(parser, options.isOverwriteDataConflicts(), monitor);
				}
				else if (options.isEquates() && name.equals("EQUATES")) {
					monitor.setMessage("Processing EQUATES ...");
					EquatesXmlMgr mgr = new EquatesXmlMgr(program, log);
					mgr.read(parser, monitor);
				}
				else if (options.isComments() && name.equals("COMMENTS")) {
					monitor.setMessage("Processing COMMENTS ...");
					CommentsXmlMgr mgr = new CommentsXmlMgr(program, log);
					mgr.read(parser, monitor);
				}
				else if (options.isProperties() && name.equals("PROPERTIES")) {
					monitor.setMessage("Processing PROPERTIES ...");
					PropertiesXmlMgr mgr = new PropertiesXmlMgr(program, log);
					mgr.read(parser, options.isOverwritePropertyConflicts(), monitor);
				}
				else if (options.isBookmarks() && name.equals("BOOKMARKS")) {
					monitor.setMessage("Processing BOOKMARKS ...");
					BookmarksXmlMgr mgr = new BookmarksXmlMgr(program, log);
					mgr.read(parser, options.isOverwriteBookmarkConflicts(), monitor);
				}
				else if (options.isTrees() && name.equals("PROGRAM_TREES")) {
					monitor.setMessage("Processing PROGRAM TREES ...");
					ProgramTreeXmlMgr mgr = new ProgramTreeXmlMgr(program, log);
					mgr.read(parser, monitor, options.isAddToProgram());
				}
				else if (options.isEntryPoints() && name.equals("PROGRAM_ENTRY_POINTS")) {
					monitor.setMessage("Processing PROGRAM ENTRY POINTS ...");
					ExtEntryPointXmlMgr mgr = new ExtEntryPointXmlMgr(program, log);
					mgr.read(parser, monitor);
				}
				else if (options.isRelocationTable() && name.equals("RELOCATION_TABLE")) {
					monitor.setMessage("Processing RELOCATION TABLE ...");
					RelocationTableXmlMgr mgr = new RelocationTableXmlMgr(program, log);
					mgr.read(parser, monitor);
				}
				else if (options.isSymbols() && name.equals("SYMBOL_TABLE")) {
					monitor.setMessage("Processing SYMBOL TABLE ...");
					SymbolTableXmlMgr mgr = new SymbolTableXmlMgr(program, log);
					mgr.read(parser, options.isOverwriteSymbolConflicts(), monitor);
					secondPassRequired = mgr.isSecondPassRequired();
				}
				else if (options.isFunctions() && name.equals("FUNCTIONS")) {
					monitor.setMessage("Processing FUNCTIONS ...");
					FunctionsXmlMgr mgr = new FunctionsXmlMgr(program, log);
					mgr.read(parser,
						!options.isAddToProgram() || options.isOverwriteSymbolConflicts(), false,
						monitor);
				}
				else if (options.isReferences() && name.equals("MARKUP")) {
					monitor.setMessage("Processing MARKUP ...");
					MarkupXmlMgr mgr = new MarkupXmlMgr(program, log);
					mgr.read(parser, options.isOverwriteReferenceConflicts(),
						options.isExternalLibraries(), options.isFunctions(),
						info.shouldProcessStack(), monitor);
				}
				else if (options.isExternalLibraries() && name.equals("EXT_LIBRARY_TABLE")) {
					monitor.setMessage("Processing EXT LIBRARY TABLE ...");
					ExternalLibXmlMgr mgr = new ExternalLibXmlMgr(program, log);
					mgr.read(parser, monitor);
				}
				else {
					monitor.setMessage("Skipping over " + name + " ...");
					parser.discardSubTree(name);
				}
			}
			parser.end(programStart);
		}
		catch (CancelledException e) {
			throw new IOException("XML Read Cancelled");
		}
		finally {
			parser.dispose();
		}

		if (secondPassRequired) {
			parser = XmlPullParserFactory.create(file, errHandler, false);
			log.setParser(parser);
			try {
				while (parser.hasNext() && !monitor.isCancelled()) {
					XmlElement element = parser.peek();
					String name = element.getName();
					if (name.equals("PROGRAM")) {
						parser.next();
					}
					else if (options.isSymbols() && name.equals("SYMBOL_TABLE")) {
						monitor.setMessage("Re-processing SYMBOL TABLE ...");
						SymbolTableXmlMgr mgr = new SymbolTableXmlMgr(program, log);
						mgr.readPass2(parser, options.isOverwriteSymbolConflicts(), monitor);
					}
					else {
						monitor.setMessage("Skipping over " + name + " ...");
						parser.discardSubTree(name);
					}
				}
			}
			catch (CancelledException e) {
				throw new IOException("XML Read Cancelled");
			}
			finally {
				parser.dispose();
			}
		}

		createDefaultTree(program, options);

		//if instructions were imported, then remove the "needs analyzed" property
		if (options.isInstructions()) {
			GhidraProgramUtilities.markProgramAnalyzed(program);
		}

		return log;
	}
Reads from the underlying XML file and populates the specified program.
@param program the program to load the XML into
@param monitor the task monitor
@param options the XML options, which features to load and to ignore
@return the message log containing any warning/error messages
@throws SAXException if an XML error occurs
@throws IOException if an I/O occurs
@throws AddressFormatException if an invalid address is specified in the XML
Ghidra/Features/Base/src/main/java/ghidra/app/util/xml/ProgramXmlMgr.java