...

Source file src/github.com/clbanning/mxj/v2/examples/getmetrics3.go

Documentation: github.com/clbanning/mxj/v2/examples

     1  // getmetrics3.go - transform Eclipse Metrics (v3) XML report into CSV files for each metric
     2  // Uses NewMapXmlReaderRaw that requires loading raw XML into a []byte buffer using a ByteReader.
     3  // Show's performance impact of copying the raw XML while simultaneously decoding it from on os.File
     4  //	Reader. (vs. getmetrics1.go) If you're processing a file and need a copy of the raw XML and SPEED,
     5  //	should buffer the file in memory and decode using mxj.NewMapXmlReaderRaw as in getmetrics4.go.
     6  
     7  /*
     8  I needed to convert a large (14.9 MB) XML data set from an Eclipse metrics report on an
     9  application that had 355,100 lines of code in 211 packages into CSV data sets.  The report
    10  included application-, package-, class- and method-level metrics reported in an element,
    11  "Value", with varying attributes.
    12  	<Value value=""/>
    13  	<Value name="" package="" value=""/>
    14  	<Value name="" source="" package="" value=""/>
    15  	<Value name="" source="" package="" value="" inrange=""/>
    16  
    17  In addition, the metrics were reported with two different "Metric" compound elements:
    18  	<Metrics>
    19  		<Metric id="" description="">
    20  			<Values>
    21  				<Value.../>
    22  				...
    23  			</Values>
    24  		</Metric>
    25  		...
    26  		<Metric id="" description="">
    27  			<Value.../>
    28  		</Metric>
    29  		...
    30  	</Metrics>
    31  
    32  To run this example, extract the metrics_data.xml file from metrics_data.zip, then:
    33  	> go run getmetrics3 -file=metrics_data.xml
    34  
    35  The output will be a set of "csv" files.
    36  */
    37  
    38  package main
    39  
    40  import (
    41  	"flag"
    42  	"fmt"
    43  	"github.com/clbanning/mxj"
    44  	"log"
    45  	"os"
    46  	"sort"
    47  	"time"
    48  )
    49  
    50  func main() {
    51  	var file string
    52  	flag.StringVar(&file, "file", "", "file to process")
    53  	flag.Parse()
    54  
    55  	fh, fherr := os.Open(file)
    56  	if fherr != nil {
    57  		fmt.Println("fherr:", fherr.Error())
    58  		return
    59  	}
    60  	defer fh.Close()
    61  	fmt.Println(time.Now().String(), "... File Opened:", file)
    62  
    63  	/*
    64  		// Get the XML data set from the file.
    65  		fs, _ := fh.Stat()
    66  		xmldata := make([]byte, fs.Size())
    67  		n, frerr := fh.Read(xmldata)
    68  		if frerr != nil {
    69  			fmt.Println("frerr:", frerr.Error())
    70  			return
    71  		}
    72  		if int64(n) != fs.Size() {
    73  			fmt.Println("n:", n, "fs.Size():", fs.Size())
    74  			return
    75  		}
    76  		fmt.Println(time.Now().String(), "... File Read - size:", fs.Size())
    77  
    78  		// load XML into a Map value
    79  		m, merr := mxj.NewMapXml(xmldata)
    80  	*/
    81  	// Consume the file using os.File Reader.
    82  	// Note: there is a single record with root tag of "Metrics".
    83  	// Also: this is MUCH slower than using buffer or not loading raw XML.
    84  	m, raw, merr := mxj.NewMapXmlReaderRaw(fh)
    85  	if merr != nil {
    86  		log.Fatal("merr:", merr.Error())
    87  	}
    88  	fmt.Println(time.Now().String(), "... XML Unmarshaled - len:", len(m))
    89  	fmt.Println("raw XML buffer size (should be same as File size):", len(raw))
    90  
    91  	// Get just the key values of interest.
    92  	// Could also use m.ValuesForKey("Metric"),
    93  	// since there's just the one path.
    94  	metricVals, err := m.ValuesForPath("Metrics.Metric")
    95  	if err != nil {
    96  		log.Fatal("err:", err.Error())
    97  	}
    98  	fmt.Println(time.Now().String(), "... ValuesFromKeyPath - len:", len(metricVals))
    99  
   100  	// now just manipulate Map entries returned as []interface{} array.
   101  	for _, v := range metricVals {
   102  		aMetricVal := v.(map[string]interface{})
   103  
   104  		// create file to hold csv data sets
   105  		id := aMetricVal["-id"].(string)
   106  		desc := aMetricVal["-description"].(string)
   107  		mf, mferr := os.OpenFile(id+".csv", os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0666)
   108  		if mferr != nil {
   109  			fmt.Println("mferr:", mferr.Error())
   110  			return
   111  		}
   112  
   113  		fmt.Print(time.Now().String(), " id: ", id, " desc: ", desc)
   114  		mf.WriteString(id + "," + desc + "\n")
   115  
   116  		// rescan looking for keys with data: Values or Value
   117  		for key, val := range aMetricVal {
   118  			switch key {
   119  			case "Values":
   120  				// extract the list of "Value" from map
   121  				values := val.(map[string]interface{})["Value"].([]interface{})
   122  				fmt.Println(" len(Values):", len(values))
   123  
   124  				// first line in file is the metric label values (keys)
   125  				var gotKeys bool
   126  				for _, vval := range values {
   127  					valueEntry := vval.(map[string]interface{})
   128  
   129  					// no guarantee that range on map will follow any sequence
   130  					lv := len(valueEntry)
   131  					list := make([][2]string, lv)
   132  					var i int
   133  					for k, v := range valueEntry {
   134  						list[i][0] = k
   135  						list[i][1] = v.(string)
   136  						i++
   137  					}
   138  					sort.Sort(mylist(list))
   139  
   140  					// extract keys as column header on first pass
   141  					if !gotKeys {
   142  						// print out the keys
   143  						var gotFirstKey bool
   144  						// for kk, _ := range valueEntry {
   145  						for i := 0; i < lv; i++ {
   146  							if gotFirstKey {
   147  								mf.WriteString(",")
   148  							} else {
   149  								gotFirstKey = true
   150  							}
   151  							// strip prepended hyphen
   152  							mf.WriteString((list[i][0])[1:])
   153  						}
   154  						mf.WriteString("\n")
   155  						gotKeys = true
   156  					}
   157  
   158  					// print out values
   159  					var gotFirstVal bool
   160  					// for _, vv := range valueEntry {
   161  					for i := 0; i < lv; i++ {
   162  						if gotFirstVal {
   163  							mf.WriteString(",")
   164  						} else {
   165  							gotFirstVal = true
   166  						}
   167  						mf.WriteString(list[i][1])
   168  					}
   169  
   170  					// terminate row of data
   171  					mf.WriteString("\n")
   172  				}
   173  			case "Value":
   174  				vv := val.(map[string]interface{})
   175  				fmt.Println(" len(Value):", len(vv))
   176  				mf.WriteString("value\n" + vv["-value"].(string) + "\n")
   177  			}
   178  		}
   179  		mf.Close()
   180  	}
   181  }
   182  
   183  type mylist [][2]string
   184  
   185  func (m mylist) Len() int {
   186  	return len(m)
   187  }
   188  
   189  func (m mylist) Less(i, j int) bool {
   190  	if m[i][0] > m[j][0] {
   191  		return false
   192  	}
   193  	return true
   194  }
   195  
   196  func (m mylist) Swap(i, j int) {
   197  	m[i], m[j] = m[j], m[i]
   198  }
   199  

View as plain text