Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/osi doxygen #1

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion README
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,16 @@ Version history:
not moving member comments before the member but keeping it after
the member instead
* these changes lead into need of enabling JAVADOC_AUTOBRIEF
- added steps for enabling the filter in Doxygen in this file
- added steps for enabling the filter in Doxygen in this file
--------------------
0.7-beta (2018-04-19) OSI
- Include changes from University of California.
- Support for all OSI *.proto files.
- Separate statement and comments to treat both parts differently (remove bugs
regarding string modifications).
- Remove "option" statements.
- Add support for "extend" statements.
- Change "repeat" from Template to standard member. --> Better collaboration
diagrams.
- Fix problems with references of nested messages (replace "." with "::").
- Change mapping from C to C++.
4 changes: 2 additions & 2 deletions doxyfile
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ PROJECT_NAME = "Protocol Buffers demo project"
# This could be handy for archiving the generated documentation or
# if some version control system is used.

PROJECT_NUMBER = 0.6-beta
PROJECT_NUMBER = 0.7-beta

# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer
Expand Down Expand Up @@ -238,7 +238,7 @@ OPTIMIZE_OUTPUT_VHDL = NO
# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions
# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen.

EXTENSION_MAPPING = proto=C
EXTENSION_MAPPING = proto=C++

# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
# to include (a tag file for) the STL sources as input, then you should
Expand Down
95 changes: 74 additions & 21 deletions proto2cpp.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
#!/usr/bin/env python
##
# Doxygen filter for Google Protocol Buffers .proto files.
# This script converts .proto files into C++ style ones
# and prints the output to standard output.
#
# version 0.6-beta
# version 0.7-beta OSI
#
# How to enable this filter in Doxygen:
# 1. Generate Doxygen configuration file with command 'doxygen -g <filename>'
Expand All @@ -12,15 +13,17 @@
# JAVADOC_AUTOBRIEF = YES
# 3. In the Doxygen configuration file, find FILE_PATTERNS and add *.proto
# FILE_PATTERNS = *.proto
# 4. In the Doxygen configuration file, find EXTENSION_MAPPING and add proto=C
# 4. In the Doxygen configuration file, find EXTENSION_MAPPING and add proto=C++
# EXTENSION_MAPPING = proto=C
# 5. In the Doxygen configuration file, find INPUT_FILTER and add this script
# INPUT_FILTER = "python proto2cpp.py"
# 6. Run Doxygen with the modified configuration
# doxygen doxyfile
#
#
# Copyright (C) 2012-2015 Timo Marjoniemi
# Version 0.7 2018 Bugfix and extensions have been made by Open Simulation Interface (OSI) Carsten Kuebler https://github.com/OpenSimulationInterface
# Copyright (C) 2016 Regents of the University of California https://github.com/vgteam/vg
# Copyright (C) 2012-2015 Timo Marjoniemi https://sourceforge.net/p/proto2cpp/wiki/Home/
# All rights reserved.
#
# This library is free software; you can redistribute it and/or
Expand Down Expand Up @@ -72,7 +75,7 @@ def __init__(self):
self.logFile = "proto2cpp.log"
## Error log file name.
self.errorLogFile = "proto2cpp.error.log"
## Logging level.
## Logging level.
self.logLevel = self.logNone

## Handles a file.
Expand All @@ -88,7 +91,7 @@ def handleFile(self, fileName):
self.log('\nXXXXXXXXXX\nXX ' + filename + '\nXXXXXXXXXX\n\n')
# Open the file. Use try to detect whether or not we have an actual file.
try:
with open(filename, 'r') as inputFile:
with open(filename, 'r', encoding='utf8') as inputFile:
self.parseFile(inputFile)
pass
except IOError as e:
Expand All @@ -97,7 +100,7 @@ def handleFile(self, fileName):
elif not fnmatch.fnmatch(filename, os.path.basename(inspect.getfile(inspect.currentframe()))):
self.log('\nXXXXXXXXXX\nXX ' + filename + '\nXXXXXXXXXX\n\n')
try:
with open(filename, 'r') as theFile:
with open(filename, 'r', encoding='utf8') as theFile:
output = ''
for theLine in theFile:
output += theLine
Expand All @@ -120,6 +123,7 @@ def handleFile(self, fileName):
def parseFile(self, inputFile):
# Go through the input file line by line.
isEnum = False
inPackage = False
# This variable is here as a workaround for not getting extra line breaks (each line
# ends with a line separator and print() method will add another one).
# We will be adding lines into this var and then print the var out at the end.
Expand All @@ -129,38 +133,87 @@ def parseFile(self, inputFile):
# block to make Doxygen detect it.
matchComment = re.search("//", line)
# Search for semicolon and if one is found before comment, add a third slash character
# ("/") and a smaller than ("<") chracter to the comment to make Doxygen detect it.
# ("/") and a smaller than ("<") character to the comment to make Doxygen detect it.
matchSemicolon = re.search(";", line)
if matchSemicolon is not None and (matchComment is not None and matchSemicolon.start() < matchComment.start()):
line = line[:matchComment.start()] + "///<" + line[matchComment.end():]
comment = "///<" + line[matchComment.end():]
# Replace '.' in nested message references with '::'
# don't work for multi-nested references and generates problems with URLs and acronyms
#comment = re.sub(r'\s(\w+)\.(\w+)\s', r' \1::\2 ', comment)
line = line[:matchComment.start()]
elif matchComment is not None:
line = line[:matchComment.start()] + "///" + line[matchComment.end():]
comment = "///" + line[matchComment.end():]
# replace '.' in nested message references with '::'
# don't work for multi-nested references and generates problems with URLs and acronyms
#comment = re.sub(r'\s(\w+)\.(\w+)\s', r' \1::\2 ', comment)
line = line[:matchComment.start()]
else:
comment = ""

# line = line.replace(".", "::") but not in quoted strings (Necessary for import statement)
line = re.sub(r'\.(?=(?:[^"]*"[^"]*")*[^"]*$)',r'::',line)

# Search for "enum" and if one is found before comment,
# start changing all semicolons (";") to commas (",").
matchEnum = re.search("enum", line)
if matchEnum is not None and (matchComment is None or matchEnum.start() < matchComment.start()):
# Search for " option ...;", remove it
line = re.sub(r'\boption\b[^;]+;', r'', line)

# Search for " package ", make a namespace
matchPackage = re.search(r"\bpackage\b", line)
if matchPackage is not None:
isPackage = True
# Convert to C++-style separator and block instead of statement
line = "namespace" + line[:matchPackage.start()] + line[matchPackage.end():].replace(";", " {")

# Search for " repeated " fields and make them ...
#matchRepeated = re.search(r"\brepeated\b", line)
#if matchRepeated is not None:
# # Convert
# line = re.sub(r'\brepeated\s+(\S+)', r' repeated \1', line)

# Search for "enum", start changing all semicolons (";") to commas (",").
matchEnum = re.search(r"\benum\b", line)
if matchEnum is not None:
isEnum = True

# Search again for semicolon if we have detected an enum, and replace semicolon with comma.
if isEnum is True and re.search(";", line) is not None:
matchSemicolon = re.search(";", line)
line = line[:matchSemicolon.start()] + "," + line[matchSemicolon.end():]

# Search for a closing brace.
matchClosingBrace = re.search("}", line[:matchComment.start()] if matchComment else line)
matchClosingBrace = re.search("}", line)
if isEnum is True and matchClosingBrace is not None:
line = line[:matchClosingBrace.start()] + "};" + line[matchClosingBrace.end():]
isEnum = False
elif isEnum is False and matchClosingBrace is not None:
# Message (to be struct) ends => add semicolon so that it'll
# be a proper C(++) struct and Doxygen will handle it correctly.
line = line[:matchClosingBrace.start()] + "};" + line[matchClosingBrace.end():]
# Search for 'message' and replace it with 'struct' unless 'message' is behind a comment.
matchMsg = re.search("message", line)
if matchMsg is not None and (matchComment is None or matchMsg.start() < matchComment.start()):
output = "struct" + line[:matchMsg.start()] + line[matchMsg.end():]
theOutput += output
else:
theOutput += line

# Replacements change start of comment...
matchMsg = re.search(r"\bmessage\b", line)
if matchMsg is not None:
line = line[:matchMsg.start()] + "struct" + line[matchMsg.end():]

# Replacements change start of comment...
matchExt = re.search(r"\bextend\b", line)
if matchExt is not None:
a_extend = line[matchExt.end():]
matchName = re.search(r"\b\w[\S:]+\b", a_extend)
if matchName is not None:
name = a_extend[matchName.start():matchName.end()]
name = re.sub(r'\w+::',r'',name)
a_extend = a_extend[:matchName.start()] + name + ": public " + a_extend[matchName.start():]
else:
a_extend = "_Dummy: public " + a_extend;
line = line[:matchExt.start()] + "struct " + a_extend

theOutput += line + comment

if isPackage:
# Close the package namespace
theOutput += "}"
isPackage = False

# Now that we've got all lines in the string let's split the lines and print out
# one by one.
# This is a workaround to get rid of extra empty line at the end which print() method adds.
Expand Down