go-hep.org/x/hep@v0.38.1/cmd/root2yoda/main.go (about)

     1  // Copyright ©2017 The go-hep Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // root2yoda converts ROOT files containing hbook-like values (H1D, H2D, ...)
     6  // into YODA files.
     7  //
     8  // Example:
     9  //
    10  //	$> root2yoda file1.root file2.root > out.yoda
    11  //	$> root2yoda -o out.yoda file1.root file2.root
    12  //	$> root2yoda -o out.yoda.gz file1.root file2.root
    13  package main // import "go-hep.org/x/hep/cmd/root2yoda"
    14  
    15  import (
    16  	"compress/gzip"
    17  	"flag"
    18  	"fmt"
    19  	"io"
    20  	"log"
    21  	"os"
    22  	"path/filepath"
    23  
    24  	"go-hep.org/x/hep/groot"
    25  	"go-hep.org/x/hep/groot/riofs"
    26  	_ "go-hep.org/x/hep/groot/riofs/plugin/http"
    27  	_ "go-hep.org/x/hep/groot/riofs/plugin/xrootd"
    28  	"go-hep.org/x/hep/hbook/yodacnv"
    29  )
    30  
    31  func main() {
    32  	log.SetFlags(0)
    33  	log.SetPrefix("root2yoda: ")
    34  
    35  	flag.Usage = func() {
    36  		fmt.Fprintf(
    37  			os.Stderr,
    38  			`Usage: root2yoda [options] file1.root [file2.root [...]] > out.yoda
    39  
    40  ex:
    41   $> root2yoda file1.root file2.root > out.yoda
    42   $> root2yoda -o out.yoda file1.root file2.root
    43   $> root2yoda -o out.yoda.gz file1.root file2.root
    44  
    45  options:
    46  `,
    47  		)
    48  		flag.PrintDefaults()
    49  	}
    50  
    51  	oname := flag.String("o", "", "path to YODA output file")
    52  
    53  	flag.Parse()
    54  
    55  	if flag.NArg() < 1 {
    56  		log.Printf("missing input ROOT file name(s)")
    57  		flag.Usage()
    58  		flag.PrintDefaults()
    59  	}
    60  
    61  	var out io.WriteCloser = os.Stdout
    62  	if *oname != "" {
    63  		f, err := os.Create(*oname)
    64  		if err != nil {
    65  			log.Fatal(err)
    66  		}
    67  		defer func() {
    68  			err = f.Close()
    69  			if err != nil {
    70  				log.Fatal(err)
    71  			}
    72  		}()
    73  		out = f
    74  		if filepath.Ext(*oname) == ".gz" {
    75  			wz := gzip.NewWriter(f)
    76  			defer func() {
    77  				err = wz.Close()
    78  				if err != nil {
    79  					log.Fatal(err)
    80  				}
    81  			}()
    82  			out = wz
    83  		}
    84  	} else {
    85  		defer out.Close()
    86  	}
    87  
    88  	for _, fname := range flag.Args() {
    89  		log.Printf("processing %s\n", fname)
    90  		f, err := groot.Open(fname)
    91  		if err != nil {
    92  			log.Fatalf("failed to open [%s]: %v\n", fname, err)
    93  		}
    94  		defer f.Close()
    95  		for _, k := range uniq(f.Keys()) {
    96  			walk(out, k)
    97  		}
    98  	}
    99  
   100  	err := out.Close()
   101  	if err != nil {
   102  		log.Fatalf("error closing output file: %v\n", err)
   103  	}
   104  }
   105  
   106  func walk(w io.Writer, k riofs.Key) {
   107  	obj := k.Value()
   108  	switch obj := obj.(type) {
   109  	case riofs.Directory:
   110  		for _, k := range uniq(obj.Keys()) {
   111  			walk(w, k)
   112  		}
   113  	case yodacnv.Marshaler:
   114  		raw, err := obj.MarshalYODA()
   115  		if err != nil {
   116  			log.Fatalf("root2yoda: error converting %v: %v", k.Name(), err)
   117  		}
   118  		_, err = w.Write(raw)
   119  		if err != nil {
   120  			log.Fatalf("root2yoda: error writing %v: %v", k.Name(), err)
   121  		}
   122  	}
   123  }
   124  
   125  func uniq(keys []riofs.Key) []riofs.Key {
   126  	set := make(map[string]riofs.Key, len(keys))
   127  	for _, k := range keys {
   128  		kk, dup := set[k.Name()]
   129  		if dup && kk.Cycle() > k.Cycle() {
   130  			continue
   131  		}
   132  		set[k.Name()] = k
   133  	}
   134  	out := make([]riofs.Key, 0, len(set))
   135  	for _, k := range set {
   136  		out = append(out, k)
   137  	}
   138  	return out
   139  }