Synopsis - Cross-Reference

File: /Synopsis/Parsers/Cxx/SXRBuffer.hh
  1//
  2// Copyright (C) 2008 Stefan Seefeld
  3// All rights reserved.
  4// Licensed to the public under the terms of the GNU LGPL (>= 2),
  5// see the file COPYING for details.
  6//
  7
  8#ifndef SXRBuffer_hh_
  9#define SXRBuffer_hh_
 10
 11#include <fstream>
 12#include <string>
 13#include <iostream>
 14
 15// An SXRBuffer generates a xml-ified version of the source file, with
 16// identifiers suitably annotated to allow linking.
 17// Since the processing of the parse tree is not monotonic (for example, 
 18// member functions are parsed only after the full class body has been seen),
 19// the entries aren't generated in-order. We collect them here, then write out
 20// the whole file once the translation has finished.
 21class SXRBuffer
 22{
 23public:
 24  struct Entry
 25  {
 26    enum Kind { SPAN, XREF};
 27    //. Less-than functor to order entries within lines by column.
 28    struct less
 29    {
 30      bool operator() (Entry const &a, Entry const &b) const
 31      { return a.column < b.column;}
 32    };
 33
 34    Entry(unsigned int c, int l, Kind k, std::string const &n, std::string const &t,
 35          std::string const &f, std::string const &d, bool a)
 36      : column(c), length(l), kind(k), name(n), type(t), from(f), description(d),
 37        continuation(a) {}
 38
 39    unsigned int column;
 40    int length;
 41    Kind kind;
 42    std::string name;
 43    std::string type;
 44    std::string from;
 45    std::string description;
 46    bool continuation;
 47  };
 48
 49  SXRBuffer(std::string const &out, std::string const &in, std::string const &name)
 50  // line numbers are reported starting from 1
 51    : line_(1), column_(0), new_line_(true)
 52  {
 53    outbuf_.open(out.c_str(), std::ios_base::out);
 54    inbuf_.open(in.c_str(), std::ios_base::in);
 55    outbuf_.sputn("<sxr filename=\"", 15);
 56    outbuf_.sputn(name.c_str(), name.size());
 57    outbuf_.sputn("\">\n", 3);
 58  }
 59  ~SXRBuffer()
 60  {
 61    outbuf_.sputn("</sxr>", 6);
 62    inbuf_.close();
 63    outbuf_.close();
 64  }
 65
 66  void insert_span(unsigned int line, unsigned int column, int length,
 67                   std::string const &type)
 68  {
 69    Line &l = lines_[line];
 70    l.insert(Entry(column, length, Entry::SPAN, "", type, "", "", false));
 71  }
 72  void insert_xref(unsigned int line, unsigned int column, unsigned int length,
 73                   std::string const &name, std::string const &type,
 74                   std::string const &from, std::string const &description,
 75                   bool continuation)
 76  {
 77    Line &l = lines_[line];
 78    l.insert(Entry(column, length, Entry::XREF, encode(name),
 79                   type, encode(from), encode(description), continuation));
 80  }
 81
 82  void write()
 83  {
 84    unsigned int line = 0;
 85    while (advance(line, 0))
 86    {
 87      Line &l = lines_[line];
 88      Line::iterator i = l.begin();
 89      while (i != l.end())
 90      {
 91        Entry const &entry = *i;
 92        advance(line, entry.column);
 93        switch (entry.kind)
 94        {
 95          case Entry::SPAN:
 96            insert("<span class=\"", 13);
 97            insert(entry.type.data(), entry.type.size());
 98            insert("\">", 2);
 99            if (entry.length == -1) finish_line();
100            else advance(line, entry.column + entry.length);
101            insert("</span>", 7);
102            break;
103          case Entry::XREF:
104            insert("<a href=\"", 9);
105            insert(entry.name.data(), entry.name.size());
106            insert("\" title=\"", 9);
107            insert(entry.description.data(), entry.description.size());
108            insert("\" from=\"", 8);
109            insert(entry.from.data(), entry.from.size());
110            insert("\" type=\"", 8);
111            insert(entry.type.data(), entry.type.size());
112            if (entry.continuation)
113              insert("\" continuation=\"true", 20);
114            insert("\">", 2);
115            if (entry.length == -1) finish_line();
116            else advance(line, entry.column + entry.length);
117            insert("</a>", 4);
118            break;
119        }
120        ++i;
121      }
122      ++line;
123    }
124  }
125
126private:
127
128  std::string encode(std::string const &n)
129  {
130    std::string retn;
131    for (std::string::const_iterator i = n.begin(); i != n.end(); ++i)
132      switch (*i)
133      {
134        case '<':
135          retn += "&lt;";
136          break;
137        case '>':
138          retn += "&gt;";
139          break;
140        case '&':
141          retn += "&amp;";
142          break;
143        case '"':
144          retn += "&quot;";
145          break;
146        default:
147          retn += *i;
148      }
149    return retn;
150  }
151
152  void insert(char const *s, unsigned int l)
153  {
154    if (new_line_)
155    {
156      outbuf_.sputn("<line>", 6);
157      new_line_ = false;
158    }
159    outbuf_.sputn(s, l);
160  }
161
162  void put(char c)
163  {
164    if (new_line_)
165    {
166      outbuf_.sputn("<line>", 6);
167      new_line_ = false;
168    }
169    switch (c)
170    {
171      case '\n':
172        outbuf_.sputn("</line>\n", 8);
173        ++line_;
174        column_ = 0;
175        new_line_ = true;
176        break;
177      case '&':
178        outbuf_.sputn("&amp;", 5);
179        ++column_;
180        break;
181      case '<':
182        outbuf_.sputn("&lt;", 4);
183        ++column_;
184        break;
185      case '>':
186        outbuf_.sputn("&gt;", 4);
187        ++column_;
188        break;
189      default:
190        outbuf_.sputc(c);
191        ++column_;
192        break;
193    }
194  }
195  
196  bool finish_line()
197  {
198    std::istreambuf_iterator<char> in(&inbuf_);
199    std::istreambuf_iterator<char> end;
200    while (in != end && *in != '\n') put(*in++);
201    return in == end;
202  }
203
204  // Advance till the given input source file position.
205  bool advance(unsigned int line, unsigned int column)
206  {
207    std::istreambuf_iterator<char> in(&inbuf_);
208    std::istreambuf_iterator<char> end;
209    while ((line_ < line || column_ < column) && in != end)
210      put(*in++);
211    return in != end;
212  }
213
214  typedef std::set<Entry, Entry::less> Line;
215  typedef std::map<int, Line> Map;
216  
217  Map lines_;
218
219  std::filebuf inbuf_;
220  std::filebuf outbuf_;
221  unsigned int line_;
222  unsigned int column_;
223  bool new_line_;
224};
225
226#endif