...

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

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

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

View as plain text