⬆️ ⬇️

Simple XML Framework - we write API for working with diagrams DIA

Simple Xml FrameworkDia Diagram Editor Dia Diagram Editor



Simple XML framework is well-known to many, with its simplicity, it is able to contend with the possibilities with a large “interfacing” JAXB, and at the same time it is compatible with Android.



Articles on its use is not "in bulk", but enough. Frejmfork was mentioned on Habré , there is an article on developerworks ibm , after all, there are good examples and a guide on the official website.

')

In general, how to use the framework is clear. But it happens that there are structures for which there are already not enough methods described in manuals and tutorials. It was this XML structure that I discovered when I began to understand how DIA stores its diagrams.



This article will explain how to teach Simple Framework to work in this situation. We will create our own "strategy" for the Simple Framework; we will follow the TreeStrategy class and describe the “tricky logic” of how to map the elements of the DIA xml file to the Java classes.



And yes, I assume that the reader is familiar with the basics of using the Simple XML Framework.



A few words about the DIA and the beginning of the story



I think the DIA diagram editor is known to almost everyone. In a way, it is a “classic of the genre”.



It was created a long time ago, but there is no more or less clear and complete description of the file format. It is known that the .dia file is a zip-archive of the xml-file with the .dia extension (by the way, the diagram editor can work with uncompressed files, you can activate saving without compression in the settings).



And then ... further, they say, "figure it out yourself, it's not difficult there."



A couple of years ago, in the mailing list, references to some more or less related descriptions were mentioned, but now they are dead.



About the presence of any software interface for generating or editing diagrams, speech either. There is a list of diagram editors that can export to the Dia format, but I haven’t found anything suitable for myself.



“Programs are good,” I thought, “but I need an API. Ready to use. Preferably in Java.



In the end, I decided to make my bike . Comfortable to me, a form under my seat,

with the wheel under the shape of my hands, with wheels in the shape of the pothole trails, which I walk.



Dia chart storage format and its features



We are not going to completely disassemble the entire structure, but go straight to the "problematic features."



In .dia, the storage structure of any element of the diagram is unified: it is an object tag that “frames” the list of attribute tags, which describe all the characteristics of an object.



For example, this is how the line element from the DIA basic palette is described.
<dia:object type="Standard - Line" version="0" id="O10"> <dia:attribute name="obj_pos"> <dia:point val="2.99224,32.237"/> </dia:attribute> <dia:attribute name="obj_bb"> <dia:rectangle val="2.92606,32.1708;7.37558,34.382"/> </dia:attribute> <dia:attribute name="conn_endpoints"> <dia:point val="2.99224,32.237"/> <dia:point val="7.3094,34.1931"/> </dia:attribute> <dia:attribute name="numcp"> <dia:int val="1"/> </dia:attribute> <dia:attribute name="end_arrow"> <dia:enum val="5"/> </dia:attribute> <dia:attribute name="end_arrow_length"> <dia:real val="0.5"/> </dia:attribute> <dia:attribute name="end_arrow_width"> <dia:real val="0.5"/> </dia:attribute> </dia:object> 


The main problem here is that all types of elements are described by one tag.

As a result, it is not clear how to tell Simple Framework that

 <dia:object type="Standard - Line" version="0" id="O10"> 
- Must be parsed into an object of the class Line, and

 <dia:object type="Standard - Box" version="0" id="O0"> 
- to the Box class object. In the standard Simple Framework toolkit I did not find how to solve this problem. That is why the “strategy” of DiaTreeStrategy was described, the creation history of which is given below.



By the way, since we are talking about the minimalism of the Simple Framework applicable to the parsing of the .dia format, it is worth mentioning the @Xpath annotation and its limitations.
The problem lies in the fact that the composition of the attributes of the diagram object that Dia stores in xml is not constant and depends on what properties you have changed for this object.



Because of this, when parsing xml structures in .dia, you cannot use the @Xpath annotation with an indication of the index to match the property of the class and the object attribute described in xml. If the name and attribute values ​​could be specified in @Xpath, as we can do in an xpath query, this would make the task easier, but alas, only the path and the index can be specified in @Xpath. These circumstances led to the appearance in my code of not the most convenient mechanism for filling in class properties from an attribute array, but about this another time.


“Strategy” in the Simple Framework, we start to write DiaTreeStrategy



A class implementing “strategy” (implementing the Strategy interface) in Simple Framework is concerned with defining the mapping between XML nodes and classes in Java.



This interface has only 2 methods - read () and write (). The first one deals with the fact that using the passed xml-node it tries to understand which class we will fill now, and the second creates an xml-node into which we will fill the properties of the object.



Writing a strategy from scratch - “it’s not a lordly affair,” especially since the usual TreeStrategy in all other respects (except for not knowing how to correctly compare classes and nodes of the object type) works quite well. Because we just fix it. Inherit and correct.



 public class DiaTreeStrategy extends TreeStrategy 


The reader probably already guessed that “everything”, what we need is to teach the “strategy” to read the type property of the object tag, and from somewhere to understand that “UML - Class” corresponds to the class diaXML.shapes.uml.UmlClass, and “ Standard Box - class diaXML.shapes.standart.StdBox.



I decided to store information about the mapping (type + version => class name) in a regular ArrayList:



 ArrayList<DiaObjToClassMapRecord> diaObj2ClassMap; 


Initialize the mapping right below the ad.
 public ArrayList<DiaObjToClassMapRecord> diaObj2ClassMap = new ArrayList<DiaObjToClassMapRecord>(); { //default base mapping - object type name and version mapped to java class name. //use "*" for 'any class' or 'any version' diaObj2ClassMap.add(new DiaObjToClassMapRecord("UML - Class", "0", "diaXML.shapes.uml.UmlClass" )); diaObj2ClassMap.add(new DiaObjToClassMapRecord("UML - Association", "2", "diaXML.shapes.uml.UmlAssociation" )); diaObj2ClassMap.add(new DiaObjToClassMapRecord("Standard - Box", "0", "diaXML.shapes.standart.StdBox" )); diaObj2ClassMap.add(new DiaObjToClassMapRecord("Standard - Text", "1", "diaXML.shapes.standart.StdText" )); diaObj2ClassMap.add(new DiaObjToClassMapRecord("Standard - ZigZagLine", "1", "diaXML.shapes.standart.StdZigZagLine" )); diaObj2ClassMap.add(new DiaObjToClassMapRecord("Standard - BezierLine", "*", "diaXML.shapes.standart.StdBezierLine" )); //universal diaObj2ClassMap.add(new DiaObjToClassMapRecord("*", "*", "diaXML.shapes.UncknownShapeObject" )); } 


As you can see, everything is quite transparent. Mapping can be extended with your own classes (if someone writes their own class for another chart element) as soon as an instance of DiaTreeStrategy is created. So far, five classes (POJOs) have been described that can initialize their properties from the attributes of a diagram object, and one universal class, UncknownShapeObject, includes all unknown objects to it; it does not initialize anything, and keeps an array of attributes unchanged.



The search mechanism in this array of mappings I rendered into a separate function
  private Class readValueAdv(Type type, NodeMap node) throws Exception { // we need here catch only <object> tag if( !"object".equals(node.getName()) ) return null; Node entry_type = node.get("type"); Node entry_version = node.get("version"); if( entry_type == null || entry_version==null) { return null; }; String name_type = entry_type.getValue(); String name_version = entry_version.getValue(); String className=null; Class expect=null; for (DiaObjToClassMapRecord crec: diaObj2ClassMap) { if ( ( crec.diaType!=null && (crec.diaType.equals(name_type) || !crec.diaType.isEmpty() && crec.diaType.equals("*") ) ) && ( crec.diaVersion!=null && ( crec.diaVersion.equals(name_version) || !crec.diaVersion.isEmpty() && crec.diaVersion.equals("*") ) ) ) { className = crec.javaClassName; break; } } if (className !=null) { expect = loader.load(className); Node entry = node.remove(label); } return expect; } 


Now all that's left is to fix the read method.



 public class DiaTreeStrategy extends TreeStrategy ... @Override public Value read(Type type, NodeMap node, Map map) throws Exception { Class actualDeclaredByDia = readValueAdv(type, node); if (actualDeclaredByDia==null) return super.read(type, node, map); return new ObjectValue(actualDeclaredByDia); } 


This is almost everything. Rather, it was a "cornerstone stumbling block" that did not allow parsing the files generated by DIA using the Simple Framework.



Naturally, in addition to what is described in the article, a lot of other work was done related to class annotation, research on how Simple XML annotations work as events in the process of serializing and parsing XML, creating service classes and so on. and so on. But this is another conversation.



What is it all for and how to use it?



In the end I would like to give a couple of examples of how to use the "DiaXML API". Without DiaTreeStrategy, it would not work. And do not forget to connect to the project diaXmlApi.jar (take here ) before building.



How to read .dia
 import java.io.File; import org.simpleframework.xml.strategy.DiaTreeStrategy; import diaXML.Diagram; import diaXML.shapes.standart.StdText; public void main(String[] args) { Strategy strategy = new DiaTreeStrategy(); Serializer serializer = new Persister(strategy); File source = new File("path/to/dia/file/to/read.dia"); Diagram probeDia=null; try { probeDia = serializer.read(Diagram.class, source); } catch (Exception e) { e.printStackTrace(); return ;} System.out.println(" File readed. Here is list of objects at layer 0 :"); for (IDiaObject cObj: probeDia.layers.get(0).objects ) { System.out.println(" dia type ["+cObj.getObjectType()+"]ver.["+cObj.getObjectTypeVersion()+"] objId:["+cObj.getId()+"] name:["+cObj.getName()+"]"); if ( StdText.TYPENAME.equals(cObj.getObjectType()) ) { System.out.println(" text value is:["+((StdText)cObj).textValue+"]"); }; } } 
How to create .dia
 import java.io.File; import org.simpleframework.xml.strategy.DiaTreeStrategy; import diaXML.Diagram; import diaXML.shapes.standart.StdText; public void main(String[] args) { Strategy strategy = new DiaTreeStrategy(); Serializer serializer = new Persister(strategy); StdText cText= new StdText(); cText.textValue="this is a demo \n of creating DIA-file"; cText.obj_pos.moveTo(15, 5); Diagram probeDia=new Diagram().initWithDefaults(); probeDia.layers.get(0).objects.add(cText); File resultFile = new File("path/to/dia/file/to/write.dia"); try { serializer.write(probeDia, resultFile); } catch (Exception e) { e.printStackTrace(); } } 


A couple more examples can be found in the source code of the project ( here ).



Conclusion



In this article, I wanted to talk about what “strategy” is in the Simple XML Framework and how to use it in a situation when staff means are not enough and about some of the limitations of the Simple Framework (for example, the fact that in the @ Xpath annotation use expressions for the names and values ​​of attributes, as we can do in @ Xpath queries).



Solving these issues has allowed us to successfully implement the key classes of the DiaXML API project with the least possible efforts. The full text of the source code can be found in the project repository.



Links





PS: Using the developed API, a utility was developed that draws a database schema (or updates a previously created one) to DIA. As a data source, a database schema in Turbine XML format is used, which Apache DDL Utils can create.

Source: https://habr.com/ru/post/318898/



All Articles