Coder Social home page Coder Social logo

merck / sonar-r-plugin Goto Github PK

View Code? Open in Web Editor NEW
24.0 8.0 19.0 323 KB

Adds support for R language into SonarQube. It uses output from lintr tool which is processed by the plugin and uploaded into SonarQube server.

License: Apache License 2.0

R 8.68% Shell 20.04% Java 71.29%
sonarqube rlanguage

sonar-r-plugin's Introduction

Sonar R Plugin

Build Status SonarCloud Status

Adds support for R language into SonarQube. Currently, it uses output from lintr tool which is processed by the plugin and uploaded into SonarQube server.

Sample Screenshot

Features

  • reporting issues found by LintR (by processing its output)

Planned Features

  • syntax highlighting
  • code coverage
  • code statistics (e.g. number of lines of code)

Extending SonarQube

Developing a plugin on SonarQube official documentation.

Run SonarQube Locally with the Plugin

Installed Java 11 is required to run SonarQube server.

# build plugin and put it into SonarQube instance
./mvnw clean package
# run SonarQube server
./sonar-local.sh console
# wait for message: SonarQube is up
# stop it by Ctrl-C

Repeat previous steps for any changes made in the plugin:

./mvnw clean package && ./sonar-local.sh console

Check logs in different terminal session:

tail -f -n 0 ./.sonar/sonarqube-*/logs/*

Web UI is running here (admin access defaults: admin/admin)

Sample Project

sample-project/README.md

Add SonarQube into a Project

Follow standard procedure to add SonarQube analysis to existing project: https://docs.sonarqube.org/display/SONAR/Analyzing+Source+Code

In case the local SonarQube instance should be used, just update SonarQube server URL to http://localhost:9000.

For example in sonar-project.properties:

sonar.host.url=http://localhost:9000

Release Process

Script for the release steps:

./release.sh

Travis CI build does the release process in Github.

What it does:

  • derive release version from current SNAPSHOT in POM
  • create new release branch
    • commit release version into POM
    • create tag
    • push
  • generate new development version by increasing last number
  • checkout master
    • commit new development version
    • push

sonar-r-plugin's People

Contributors

erictummers avatar flzara avatar jasonmvictor avatar kenahoo avatar kmoco2am avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

sonar-r-plugin's Issues

A couple of usage questions

Hi there, I'm interested in using lintr (or some other R code quality tool, but I don't know of any others) with Sonarqube, so your project looks intriguing. A couple of questions:

  1. https://sonarsource.bintray.com/Distribution/sonarqube/sonarqube-6.7.4.zip doesn't seem to be available anymore, the base URL has changed to https://binaries.sonarsource.com. I also wanted to fetch a later version - would you recommend going with 6.7.6, or trying 7.5? Same issue happens with

  2. I can't quite tell in the code where the lintr_out.json file is actually used. If I want to change the name of that file, is there a config somewhere I'll need to change?

  3. When I run the example commands in sample-project/README.md, it completes successfully but doesn't seem to actually upload any of the issues from lintr_out.json. See screenshot. Something wrong in my setup?

screen shot 2018-12-28 at 12 00 27 am

Not able to find a InputFile with <pathtofile>/file.R

I have installed your plugin in SonarQube 7.9.2 and I am receiving the following error:

Not able to find a InputFile with /path/to/the/file.R

Note: not the real path

This error is repeated 19 times.
So in my logs, here's what I see:

INFO: Sensor LintR Sensor for R files [r]
INFO: Running sensor for LintR
INFO: Issues found by LintR: 19
ERROR: Not able to find a InputFile with /path/to/the/file.R
ERROR: Not able to find a InputFile with /path/to/the/file.R
ERROR: Not able to find a InputFile with /path/to/the/file.R
ERROR: Not able to find a InputFile with /path/to/the/file.R
ERROR: Not able to find a InputFile with /path/to/the/file.R
ERROR: Not able to find a InputFile with /path/to/the/file.R
ERROR: Not able to find a InputFile with /path/to/the/file.R
ERROR: Not able to find a InputFile with /path/to/the/file.R
ERROR: Not able to find a InputFile with /path/to/the/file.R
ERROR: Not able to find a InputFile with /path/to/the/file.R
ERROR: Not able to find a InputFile with /path/to/the/file.R
ERROR: Not able to find a InputFile with /path/to/the/file.R
ERROR: Not able to find a InputFile with /path/to/the/file.R
ERROR: Not able to find a InputFile with /path/to/the/file.R
ERROR: Not able to find a InputFile with /path/to/the/file.R
ERROR: Not able to find a InputFile with /path/to/the/file.R
ERROR: Not able to find a InputFile with /path/to/the/file.R
ERROR: Not able to find a InputFile with /path/to/the/file.R
ERROR: Not able to find a InputFile with /path/to/the/file.R
INFO: Sensor LintR Sensor for R files [r] (done) | time=108ms

It appears to me that the json file is being found and read to know that there are 19 issues, which is the correct number. But when it tries to read the file.R itself, it can't seem to find/access it.

The file.R exists in the same directory as the lintr_out.json. Both files have the same permissions applied.

Have you run across this issue before? Please ask questions if you need more info. I didn't want to provide too much unsolicited information.

Not able to find a InputFile with R/tests/testthat.R

The plugin is able to find and lint source files, but not able to find test files:

ERROR: Not able to find a InputFile with R/tests/testthat.R

However, in the logs I can see that this test file was indexed correctly:

'R/tests/testthat.R' indexed as test with language 'r' 

Also, I made sure to define the properties in sonar-project.properties:

sonar.sources=R/R/
sonar.tests=R/tests/

In the source of the plugin, I can see in the function processIssue in file https://github.com/Merck/sonar-r-plugin/blob/master/src/main/java/com/msd/gin/common/sonar/LintRSensor.java#L97 that the InputFile type expected is MAIN InputFile.Type.MAIN (and not TEST), which makes me think that the test files are excluded from linting.

This is even more surprising because there are test files in the json file in the sample-project (https://github.com/Merck/sonar-r-plugin/blob/master/sample-project/lintr_out.json#L390).

Please advise.

Exec format error

Hi
we tried to set up a sonar-r-plugin but we faced difficulty while setting up

Error: sonar-r-plugin/.sonar/sonarqube-7.9.1/bin/macosx-universal-64/./wrapper: Exec format error

Can you please help us here

Unable to build the package

Hi
I am trying to add R Plugin package in Sonar Qube. I have installed HJDK Java 11 on the server as well. When I try to build plugin using ./mvnw clean package. It fails with Java error. Any Idea. I tried to copy the certificates as well from Java 11 to already installed Java 8. Belwo is the error:-

C:\temp\sonar-r-plugin-master>mvnw.cmd clean package
Exception in thread "main" javax.net.ssl.SSLHandshakeException: sun.security.val
idator.ValidatorException: PKIX path building failed: sun.security.provider.cert
path.SunCertPathBuilderException: unable to find valid certification path to req
uested target
at sun.security.ssl.Alerts.getSSLException(Unknown Source)
at sun.security.ssl.SSLSocketImpl.fatal(Unknown Source)
at sun.security.ssl.Handshaker.fatalSE(Unknown Source)
at sun.security.ssl.Handshaker.fatalSE(Unknown Source)
at sun.security.ssl.ClientHandshaker.serverCertificate(Unknown Source)
at sun.security.ssl.ClientHandshaker.processMessage(Unknown Source)
at sun.security.ssl.Handshaker.processLoop(Unknown Source)
at sun.security.ssl.Handshaker.process_record(Unknown Source)
at sun.security.ssl.SSLSocketImpl.readRecord(Unknown Source)
at sun.security.ssl.SSLSocketImpl.performInitialHandshake(Unknown Source
)
at sun.security.ssl.SSLSocketImpl.startHandshake(Unknown Source)
at sun.security.ssl.SSLSocketImpl.startHandshake(Unknown Source)
at sun.net.www.protocol.https.HttpsClient.afterConnect(Unknown Source)
at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect
(Unknown Source)
at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(Unknown S
ource)
at sun.net.www.protocol.http.HttpURLConnection.getInputStream(Unknown So
urce)
at sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(Unkn
own Source)
at org.apache.maven.wrapper.DefaultDownloader.downloadInternal(DefaultDo
wnloader.java:90)
at org.apache.maven.wrapper.DefaultDownloader.download(DefaultDownloader
.java:76)
at org.apache.maven.wrapper.Installer.createDist(Installer.java:72)
at org.apache.maven.wrapper.WrapperExecutor.execute(WrapperExecutor.java
:121)
at org.apache.maven.wrapper.MavenWrapperMain.main(MavenWrapperMain.java:
61)
Caused by: sun.security.validator.ValidatorException: PKIX path building failed:
sun.security.provider.certpath.SunCertPathBuilderException: unable to find vali
d certification path to requested target
at sun.security.validator.PKIXValidator.doBuild(Unknown Source)
at sun.security.validator.PKIXValidator.engineValidate(Unknown Source)
at sun.security.validator.Validator.validate(Unknown Source)
at sun.security.ssl.X509TrustManagerImpl.validate(Unknown Source)
at sun.security.ssl.X509TrustManagerImpl.checkTrusted(Unknown Source)
at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(Unknown Sour
ce)
... 18 more
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to
find valid certification path to requested target
at sun.security.provider.certpath.SunCertPathBuilder.build(Unknown Sourc
e)
at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(Unknown
Source)
at java.security.cert.CertPathBuilder.build(Unknown Source)
... 24 more

C:\temp\sonar-r-plugin-master>%JAVA_HOME%
'C:\Program' is not recognized as an internal or external command,
operable program or batch file.

C:\temp\sonar-r-plugin-master>

Thanks

Generate Release 0.2.3 to include StringUtils fix

Hi there,

I've noticed bfffd7b appears to include a fix for issue I've hit when using latest 0.2.2 Release when trying to run from Azure DevOps.

##[error]ERROR: Error during SonarScanner execution
##[debug]Processed: ##vso[task.logissue type=error;]ERROR: Error during SonarScanner execution
ERROR: Error during SonarScanner execution
##[error]java.lang.NoClassDefFoundError: org/sonar/api/internal/apachecommons/lang/StringUtils
##[debug]Processed: ##vso[task.logissue type=error;]java.lang.NoClassDefFoundError: org/sonar/api/internal/apachecommons/lang/StringUtils
java.lang.NoClassDefFoundError: org/sonar/api/internal/apachecommons/lang/StringUtils
##[error]at com.msd.gin.common.sonar.LintRSensor.readLintrOutputFile(LintRSensor.java:88)
##[debug]Processed: ##vso[task.logissue type=error;]at com.msd.gin.common.sonar.LintRSensor.readLintrOutputFile(LintRSensor.java:88)
	at com.msd.gin.common.sonar.LintRSensor.readLintrOutputFile(LintRSensor.java:88)
##[error]at com.msd.gin.common.sonar.LintRSensor.execute(LintRSensor.java:67)

Appears there is no release that includes above fix yet.
I built out latest of master locally and this has resolved my issue, so may benefit others if a new release is created.

Code line count

Hello and thanks for providing this plugin.
I've managed to setup the plugin, and using lintr provides input for SQ, at least in the code smell category. However, there seems to be node line count of R code in SQ. Is this a limitation, or did I miss something?
Thanks

Analyze R code in Jenkins

I have set up a workflow to analyze R code in Jenkins, the result is sometimes different, the same code can lead to opposite build results. What is the exception with the zero pointer mentioned in the sonar analysis step here? ->

    ERROR: Error during SonarScanner execution

java.lang.NullPointerException
at java.base/java.util.Objects.requireNonNull(Unknown Source)
at java.base/java.util.Arrays$ArrayList.(Unknown Source)
at java.base/java.util.Arrays.asList(Unknown Source)
at com.msd.gin.common.sonar.lintr.JsonOutputParser.parse(JsonOutputParser.java:33)
at com.msd.gin.common.sonar.LintRSensor.execute(LintRSensor.java:76)
at org.sonar.scanner.sensor.AbstractSensorWrapper.analyse(AbstractSensorWrapper.java:48)
at org.sonar.scanner.sensor.ModuleSensorsExecutor.execute(ModuleSensorsExecutor.java:85)
at org.sonar.scanner.sensor.ModuleSensorsExecutor.lambda$execute$1(ModuleSensorsExecutor.java:59)
at org.sonar.scanner.sensor.ModuleSensorsExecutor.withModuleStrategy(ModuleSensorsExecutor.java:77)
at org.sonar.scanner.sensor.ModuleSensorsExecutor.execute(ModuleSensorsExecutor.java:59)
at org.sonar.scanner.scan.ModuleScanContainer.doAfterStart(ModuleScanContainer.java:82)
at org.sonar.core.platform.ComponentContainer.startComponents(ComponentContainer.java:137)
at org.sonar.core.platform.ComponentContainer.execute(ComponentContainer.java:123)
at org.sonar.scanner.scan.ProjectScanContainer.scan(ProjectScanContainer.java:392)
at org.sonar.scanner.scan.ProjectScanContainer.scanRecursively(ProjectScanContainer.java:388)
at org.sonar.scanner.scan.ProjectScanContainer.doAfterStart(ProjectScanContainer.java:357)
at org.sonar.core.platform.ComponentContainer.startComponents(ComponentContainer.java:137)
at org.sonar.core.platform.ComponentContainer.execute(ComponentContainer.java:123)
at org.sonar.scanner.bootstrap.GlobalContainer.doAfterStart(GlobalContainer.java:150)
at org.sonar.core.platform.ComponentContainer.startComponents(ComponentContainer.java:137)
at org.sonar.core.platform.ComponentContainer.execute(ComponentContainer.java:123)
at org.sonar.batch.bootstrapper.Batch.doExecute(Batch.java:72)
at org.sonar.batch.bootstrapper.Batch.execute(Batch.java:66)
at org.sonarsource.scanner.api.internal.batch.BatchIsolatedLauncher.execute(BatchIsolatedLauncher.java:46)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.base/java.lang.reflect.Method.invoke(Unknown Source)
at org.sonarsource.scanner.api.internal.IsolatedLauncherProxy.invoke(IsolatedLauncherProxy.java:60)
at com.sun.proxy.$Proxy0.execute(Unknown Source)
at org.sonarsource.scanner.api.EmbeddedScanner.doExecute(EmbeddedScanner.java:189)
at org.sonarsource.scanner.api.EmbeddedScanner.execute(EmbeddedScanner.java:138)
at org.sonarsource.scanner.cli.Main.execute(Main.java:112)
at org.sonarsource.scanner.cli.Main.execute(Main.java:75)
at org.sonarsource.scanner.cli.Main.main(Main.java:61)

missing lintr_out.json?

Hello,
I have built this plugin and deployed it into our sonarqube server. When I run the scan of a R script, the sonar scanner throws this error:

INFO: Sensor LintR Sensor for R files [r]
INFO: Running sensor for LintR
WARN: Cannot read lintr_out.json file, skipping
java.nio.file.NoSuchFileException: lintr_out.json
at java.base/sun.nio.fs.UnixException.translateToIOException(Unknown Source)
at java.base/sun.nio.fs.UnixException.rethrowAsIOException(Unknown Source)
at java.base/sun.nio.fs.UnixException.rethrowAsIOException(Unknown Source)
at java.base/sun.nio.fs.UnixFileSystemProvider.newByteChannel(Unknown Source)
at java.base/java.nio.file.Files.newByteChannel(Unknown Source)
at java.base/java.nio.file.Files.newByteChannel(Unknown Source)
at java.base/java.nio.file.Files.readAllBytes(Unknown Source)
at com.msd.gin.common.sonar.LintRSensor.execute(LintRSensor.java:69)
at org.sonar.scanner.sensor.AbstractSensorWrapper.analyse(AbstractSensorWrapper.java:48)
at org.sonar.scanner.sensor.ModuleSensorsExecutor.execute(ModuleSensorsExecutor.java:85)
at org.sonar.scanner.sensor.ModuleSensorsExecutor.lambda$execute$1(ModuleSensorsExecutor.java:59)
at org.sonar.scanner.sensor.ModuleSensorsExecutor.withModuleStrategy(ModuleSensorsExecutor.java:77)
at org.sonar.scanner.sensor.ModuleSensorsExecutor.execute(ModuleSensorsExecutor.java:59)
at org.sonar.scanner.scan.ModuleScanContainer.doAfterStart(ModuleScanContainer.java:82)
at org.sonar.core.platform.ComponentContainer.startComponents(ComponentContainer.java:136)
at org.sonar.core.platform.ComponentContainer.execute(ComponentContainer.java:122)
at org.sonar.scanner.scan.ProjectScanContainer.scan(ProjectScanContainer.java:400)
at org.sonar.scanner.scan.ProjectScanContainer.scanRecursively(ProjectScanContainer.java:395)
at org.sonar.scanner.scan.ProjectScanContainer.doAfterStart(ProjectScanContainer.java:358)
at org.sonar.core.platform.ComponentContainer.startComponents(ComponentContainer.java:136)
at org.sonar.core.platform.ComponentContainer.execute(ComponentContainer.java:122)
at org.sonar.scanner.bootstrap.GlobalContainer.doAfterStart(GlobalContainer.java:141)
at org.sonar.core.platform.ComponentContainer.startComponents(ComponentContainer.java:136)
at org.sonar.core.platform.ComponentContainer.execute(ComponentContainer.java:122)
at org.sonar.batch.bootstrapper.Batch.doExecute(Batch.java:73)
at org.sonar.batch.bootstrapper.Batch.execute(Batch.java:67)
at org.sonarsource.scanner.api.internal.batch.BatchIsolatedLauncher.execute(BatchIsolatedLauncher.java:46)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.base/java.lang.reflect.Method.invoke(Unknown Source)
at org.sonarsource.scanner.api.internal.IsolatedLauncherProxy.invoke(IsolatedLauncherProxy.java:60)
at com.sun.proxy.$Proxy0.execute(Unknown Source)
at org.sonarsource.scanner.api.EmbeddedScanner.doExecute(EmbeddedScanner.java:189)
at org.sonarsource.scanner.api.EmbeddedScanner.execute(EmbeddedScanner.java:138)
at org.sonarsource.scanner.cli.Main.execute(Main.java:112)
at org.sonarsource.scanner.cli.Main.execute(Main.java:75)
at org.sonarsource.scanner.cli.Main.main(Main.java:61)

INFO: Sensor LintR Sensor for R files [r] (done) | time=2ms

This is the sonar-scanner command I am running:

sonar-scanner -Dsonar.projectKey=rtest -Dsonar.host.url=http://sonarqube.domain.com -Dsonar.login= -Dsonar.sources=.

Do I need to run lintr and direct output to lintr_out.json prior executing the scan?

Missing linters

A lot of linters trigger errors in SonarQube, e.g. seq_linter.

Here are the linters that I had to remove (commented in the code) in order to make it work with the current plugin:

lint_dir(path = '.',
            linters = list(
                    # T_and_F_symbol_linter,  # yields SQ error
                    assignment_linter,
                    closed_curly_linter,
                    commas_linter,
                    commented_code_linter,
                    todo_comment_linter,
                    # cyclocomp_linter,  # yields SQ error
                    # object_name_linter,  # yields local error
                    object_length_linter,
                    camel_case_linter,
                    # equals_na_linter,  # yields SQ error
                    # extraction_operator_linter,  # yields SQ error
                    # function_left_parentheses_linter,  # yields SQ error
                    # implicit_integer_linter,  # yields SQ error
                    infix_spaces_linter,
                    line_length_linter,
                    no_tab_linter,
                    object_usage_linter,
                    open_curly_linter,
                    # paren_brace_linter,  # yields SQ error
                    absolute_path_linter,
                    nonportable_path_linter,
                    # pipe_continuation_linter,  # yields SQ error
                    semicolon_terminator_linter,
                    # seq_linter,  # yields SQ error
                    single_quotes_linter,
                    spaces_inside_linter,
                    spaces_left_parentheses_linter,
                    trailing_blank_lines_linter,
                    trailing_whitespace_linter,
                    undesirable_function_linter,
                    undesirable_operator_linter
                    # unneeded_concatenation_linter  # yields SQ error
    )
)

Have I done something wrong or are the linters not supported? If they are not supported, are there plans to support all the linters?

Markdown

Does the plugin sense and lint Rmarkdown files? (.Rmd)

Jenkins execution : Getting error WARN: Cannot read lintr_out.json file when trying to execute job using SonarScanner

I have configured R sonar plugin in sonarqube server and installed R in my jenkins nodes. But when tried to execute job from jenkins, job got failed with error "WARN: Cannot read lintr_out.json file, skipping
java.nio.file.NoSuchFileException: lintr_out.json"

log :
NFO: Load project repositories (done) | time=420ms
INFO: 1456 files indexed
INFO: 0 files ignored because of inclusion/exclusion patterns
INFO: 0 files ignored because of scm ignore settings
INFO: Quality profile for r: Sonar way
INFO: ------------- Run sensors on module SonarTest
INFO: Load metrics repository
INFO: Load metrics repository (done) | time=29ms
INFO: Sensor JavaXmlSensor [java]
INFO: Sensor JavaXmlSensor [java] (done) | time=5ms
INFO: Sensor HTML [web]
INFO: Sensor HTML [web] (done) | time=21ms
INFO: Sensor JaCoCo XML Report Importer [jacoco]
INFO: Sensor JaCoCo XML Report Importer [jacoco] (done) | time=10ms
INFO: Sensor LintR Sensor for R files [r]
INFO: Running sensor for LintR
WARN: Cannot read lintr_out.json file, skipping
java.nio.file.NoSuchFileException: lintr_out.json
at sun.nio.fs.UnixException.translateToIOException(UnixException.java:86)
at sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:102)
at sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:107)
at sun.nio.fs.UnixFileSystemProvider.newByteChannel(UnixFileSystemProvider.java:214)
at java.nio.file.Files.newByteChannel(Files.java:361)
at java.nio.file.Files.newByteChannel(Files.java:407)
at java.nio.file.Files.readAllBytes(Files.java:3152)
at com.msd.gin.common.sonar.LintRSensor.execute(LintRSensor.java:69)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.