Summary
The intention of QAvalidator is:
-
Document the intended architecture.
-
Validate that the intended architecture is always followed. Issue warnings or errors if it isn’t.
-
Visualize the architecture — both the intended architecture and the actual dependencies.
-
Allow to explore the actual relations on architecture level and on code level in an interactive way.
The validation checks are easily possible for each developer on the developer machine, and they can be done in the CI builds to give immediate feedback.
The term "architecture" is used in a very code-centered way. QAvalidator is about dependencies on code level.
QAvalidator Analysis
QAvalidator uses multiple information sources:
-
Java code relationships, taken in a static way from the
.class
files -
Maven dependencies as defined in the
pom.xml
files -
Manually defined architectures
QAvalidator defines its own DSLs for both the architecture model description and the analysis description. Chapters Architecture Definition and Rules and Analysis DSL explain the details.
There is also a sonar plugin available.
1. Getting Started
This chapter provides examples that you can copy/paste to get started with QAvalidator.
-
Using it: via Maven, Gradle, or command line
-
An example for the QAvalidator AnalysisDSL
1.2. Maven Usage
The simplest way to use QAvalidator is via Maven.
Just include the Maven plugin into your pom.xml:
<properties>
<qav.version>1.2.10</qav.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>de.qaware.qav</groupId>
<artifactId>qav-maven-plugin</artifactId>
<version>${qav.version}</version>
<executions>
<execution>
<goals>
<goal>qav</goal>
</goals>
<!-- use this in multi-module builds to avoid that
QAvalidator is being executed for each sub-project -->
<inherited>false</inherited>
</execution>
</executions>
</plugin>
</plugins>
</build>
Call the QAvalidator run with this command:
mvn qav:qav
or
mvn de.qaware.qav:qav-maven-plugin:qav
In this default configuration, QAvalidator checks for the file qa/analysis.groovy
. If the file exists, QAvalidator uses it.
If it doesn’t, QAvalidator runs the default analysis which performs the following analysis steps:
-
QAvalidator reads all the
.class
files from the project where the plugin is defined (and called), and all of its subprojects. -
It checks for package cycles.
-
It writes its results into the directory
target/qav-report
.
|
In any case, before calling a QAvalidator analysis, the plugin analyzes the Maven project and its dependencies, and writes out the dependency graph in |
1.3. Define Your Own Analysis
Defining the analysis run requires to write a little script using the QAvalidator’s Analysis DSL.
We’ll use the default analysis and see what it does.
Put this file in qa/analysis.groovy
.
apply "de.qaware.qav.analysis.plugins.ShortcutQavPlugin" (1)
analysis("Step 1: Analyze Package Architecture") { (2)
// Prepare input: analyze the graph and create an architecture tree, based on the package hierarchy:
// packageGraph is a filtered graph which only contains those nodes which belong to the package architecture
// (i.e. it does not contain the class nodes).
def packageGraph = createPackageArchitectureView(inputClassesGraph) (3)
def packageCycleGraph = findCycles(packageGraph, "Package") (4)
// output:
printNodes(packageCycleGraph, "packageCycleNodes.txt") (5)
writeDot(packageGraph, "packageGraph", architecture("Package")) (6)
writeDot(packageCycleGraph, "packageCycleGraph", architecture("Package")) (7)
writeGraphLegend() (8)
writeFile(dependencyGraph, "dependencyGraph.json") (9)
}
Here is what happens:
-
The
ShortcutQavPlugin
offers some commands we’ll use in this script. Using theapply
command makes them available. -
An analysis run consists of one or more steps. This analysis has only one step. QAvalidator executes them in an order, and tries to recover from errors in each step.
-
The command
createPackageArchitectureView
creates an architecture tree based on the package hierarchy. In this case, it works on theinputClassesGraph
which is a graph that represents all classes in the input scope, and no external classes referenced by them.packageGraph
is now a filtered graph which only contains those nodes which belong to the package architecture (i.e. it does not contain nodes representing the classes). -
Find cycles on the given graph.
-
Prints out all nodes which belong to a cycle into a text file.
-
Writes out the package graph as DOT file, and tries to generate a
.png
and a.svg
file by callingdot
, ifgraphviz
is installed (See [GraphViz]). Fails ifgraphviz
is not installed. Also exports a.graphml
file which can be used with [yEd]. -
Do the same with only those packages which are part of a cycle.
-
Writes out a legend which explains which arrows represent which dependency type.
-
Writes out the graph as JSON file, to be loaded e.g. in the Web UI.
Step 4 will mark the step as "failed" if there are any cycles.
If you also want the QAvalidator Plugin to break you build, add the following in the Maven plugin configuration,
after the <executions>
block, within the <plugin>
definition:
<configuration>
<failOnError>true</failOnError>
</configuration>
1.4. Maven Configuration
Here is the full configuration with the Maven Plugin. The example here shows the default values. Override them as needed.
<configuration>
<analysisFilename>qa/analysis.groovy</analysisFilename>
<failOnError>false</failOnError>
<useDefaultInputDirs>true</useDefaultInputDirs>
<outputDir>target/qav-report</outputDir>
</configuration>
If no analysisFilename
is given, QAvalidator will try these locations:
-
qa/analysis.groovy
-
src/qa/analysis.groovy
-
classpath:/default_analysis.groovy
1.5. Gradle Usage
The Gradle Plugin is applied like this:
buildscript {
repositories {
jcenter()
}
dependencies {
classpath "de.qaware.qav:qav-gradle-plugin:1.2.7"
}
}
apply plugin: "de.qaware.qav.gradle.qavalidator"
// these are the default values:
qavalidator {
useDefaultInputDirs = true
failOnError = false
analysisFilename = "qa/analysis.groovy"
outputDir = "build/qav-report"
}
The configuration shown above shows the default values.
If no analysisFilename
is given, QAvalidator will try these locations:
-
qa/analysis.groovy
-
src/qa/analysis.groovy
-
classpath:/default_analysis.groovy
Call the QAvalidator run with this command:
gradlew qavalidator
|
In any case, before calling a QAvalidator analysis, the Gradle plugin analyzes the Gradle project and its dependencies, and writes out the dependency graph in |
1.6. Command Line Usage
There is also a command line tool. It needs the analysis file (mandatory), the output directory (optional), and the input directories (also optional) — if the optional arguments are not given on the command line, they must be defined in the analysis file.
$ java -jar qav-app-<version>.jar --analysis=<analysis-file> \
[--outputDir=<output-dir>] [input-dirs ...]
|
Do not omit the "=" sign in the command line parameters, and do not put whitespace around it. The input-dirs are non-option arguments. |
If no analysis
filename is given, QAvalidator will try these locations:
-
qa/analysis.groovy
-
src/qa/analysis.groovy
-
classpath:/default_analysis.groovy
1.7. Using with Hierarchical Projects
The QAvalidator Maven plugin handles both single-module and hierarchical multi-module projects.
Define the plugin and the qa/analysis.groovy
file in the top-level project that you which to analyze.
That’s the project where you call mvn qav:qav
.
The following figure gives an example.
my-app
|-- qa
| \-- analysis.groovy
|-- my-app-parent
| \-- pom.xml // this is the parent pom for all other sub-projects.
|-- my-app-api
| |-- src/...
| \-- pom.xml
|-- my-app-client
| |-- src/...
| \-- pom.xml
|-- my-app-impl
| |-- src/...
| \-- pom.xml
|-- my-app-util
| |-- src/...
| \-- pom.xml
\-- pom.xml // define the QAvalidator plugin here
|
It’s a best practice to have a separate parent pom which defines the dependency management etc. If |
Alternatively, it’s possible to do separate, disjunct analysis on sub projects. Then, you need to define the QAvalidator plugin in the sub projects, as shown in this example:
my-app
|-- my-app-api
| |-- src/...
| \-- pom.xml
|-- my-app-client
| |-- src/...
| | |-- qa
| | | \-- analysis.groovy
| \-- pom.xml // define the QAvalidator plugin here
|-- my-app-impl
| |-- qa
| | \-- analysis.groovy
| |-- src/...
| \-- pom.xml // define the QAvalidator plugin here
|-- my-app-util
| |-- src/...
| \-- pom.xml
\-- pom.xml
2. Architecture Definition and Rules
This chapter explains some of the underlying ideas and concepts that lead to the design of QAvalidator.
2.1. Definitions
QAvalidator’s uses a code-centered, static approach to describe software systems. Its architecture model is built on the following concepts:
- System
-
A system is the sum of all classes and their relations in the given input scope. An input scope is a set of classes — usually all the classes of a project, e.g. like a Maven module with all its sub modules.
- Architecture and Architecture View
-
There is one or more architectures or architecture views to describe a system. An architecture view is a hierarchy of components. It is possible to have more than one architecture view to express different considerations. Typical examples are the technical view with a layered architecture, and a business view with vertical slices per business component.
We’ll use the terms architecture and architecture view as synonyms. - Component
-
-
A component has one or more interfaces (APIs) and an implementation. The APIs are public, the implementation is a private detail of the component.
-
A component may use other components. To be precise: a component may use other components' APIs — it may never use their implementations directly.
-
A component may contain other components, forming a hierarchy. We also speak of nested components.
-
- Class
-
A class is the detailed level to look at a system. A software system is constructed by combining many classes. Classes have relations to other classes: they use them, inherit from them, etc.
A class is the atomic element QAvalidator works with. This is an arbitrary decision — one could also look at methods or instructions. That would make sense e.g. when analyzing data flow etc; however, we’re interested in the structure of a system. On the other hand, it’s not hard-coded into QAvalidator to look at classes. In fact, QAvalidator works with graphs and trees, and consequently abstracts from the type of the atomic element of analysis.
As we’ll see in the sections Architecture DSL Example and Architecture Model DSL Reference, QAvalidator directly translates these concepts into its Architecture DSL.
2.1.1. Using Graphs to Model Systems, Architectures, and Components
We use plain graphs and hierarchical graphs to model systems, architectures, and components.
- Graph
-
A graph consists of nodes (aka: vertices) and edges (aka: dependencies, relations). We use directed graphs, i.e. the edges have a direction with a source and target.
- Hierarchical Graph
-
A hierarchical graph combines a graph and a hierarchical tree by assigning the nodes in the graph to the leaves of the tree.
(TODO: add a figure.)
To model a software system with a hierarchical graph, the nodes in the graph represent classes, and the edges in the graph represent relations of different types. The graph represents the system as it really is, with the real dependencies as they are implemented in the code.
An architecture view is a hierarchy of components. This is the tree part of the hierarchical graph. The leaf components have classes assigned which implement them.
The architecture hierarchy can either be used to explain the intended architecture; QAvalidator can be used to check if there is a gap between plan and reality. Or QAvalidator can be used the other way around to visualize the structure of the system in a graphical, textual, or interactive way.
There may be one or more architecture views. The architecture views are independent of one another. Apart from being associated with the same nodes in the graph, they don’t have anything to do with each other.
2.1.2. Literature
For some background on this architecture model, see also [AP11].
2.1.3. Limitations
QAvalidator is strictly focused on the code view. QAvalidator does not, for example, consider runtime properties like number of instances of a class, number of calls to a component, or performance properties like CPU or memory usage. It also does not describe deployment views of a software system.
2.2. Architecture DSL Example
This is an example architecture definition:
architecture(name: "T-View", prefix: "tview", reflexMLversion: "1.0") {
excludes "java.lang.**", "java.util.**", "org.slf4j.**"
component ("Monitor") {
// uses "IMail" --> is allowed implicitly, because the components are siblings
impl "de.qaware.mail.monitor.**"
}
component ("Mail") {
api "IMail" : "de.qaware.mail.api.**"
impl "de.qaware.mail.impl.**", "de.qaware.mail.impl2.**"
component ("MailSender") {
api "de.qaware.mail.sender.api.**"
impl "de.qaware.mail.sender.impl.JavaMailSender"
uses "JavaMail"
}
}
component ("3rdParty") {
component("JavaMail") {api "javax.mail.**"}
}
}
2.3. Architecture Model DSL Reference
This section contains the detailed definition of the Architecture DSL.
The Architecture DSL is a Groovy DSL; this explains most of the syntax we use. Valid Groovy code is possible anywhere.
2.3.1. Keywords
These are the QAvalidator Architecture DSL Keywords:
- architecture
-
architecture
has three attributes to be set: Thename
is used in reports, theprefix
is required for technical purpuses (like the DOT export), and the attributereflexMLversion
indicates the DSL version being used. In curly braces, it accepts the same declarations as components.
In each architecture file, there may be one or more architecture definitions. - component
-
component
describes a component as defined in section Definitions. It has one parameter which is the component’s name; in curly braces it defines the component’s properties: api, impl, uses, ignore. - api
-
There is one ore more API per component. Each API has a name; if there is only one API, its name defaults to the component’s name. The API name must be unique for the system — not only within that component.
There are two forms to define an API: With name, it’s the "colon" syntax with the name of the API, then a colon, and then a list of regular expressions defining matching classes. (For Groovy guys: This is the map syntax with a String as key, and a List of Strings as value.)
api "adminApi" : "com.mycompany.comp1.api.admin.*" api "userApi" : "com.mycompany.comp1.api.user.*", "com.mycompany.comp1.api.settings.*"
Without name, it’s just a list of regular expressions defining matching classes. This api will automatically get the name of its component.
api "com.mycompany.comp1.api.admin.*", "com.mycompany.comp1.api.user.*", "com.mycompany.comp1.api.settings.*"
- impl
-
The
impl
keyword defines the component’s implementation. It works similar to theapi
keyword, but there is only one implementation per component. Therefore, there is no name for an implementation, and theimpl
keyword just takes a list of regular expressions, defining all implementation classes. - uses
-
The keyword
uses
defines all APIs this component uses. It receives a list of all API names that it may use. It’s allowed to define theuses
keyword more than once.
Note that this keyword takes the list of API names, not component names. Note also that for components which offer only one API, the API name defaults to the component’s name. - usesImpl
-
The keyword
usesImpl
defines all implementation parts this component may use. It’s allowed to define theusesImpl
keyword more than once. This feature exists to be able to define factories which need to create instances of a specific implementation class; it is also useful e.g. for Spring 4 configurations. - excludes
-
(Formerly also:
ignore
.) The keywordexcludes
allows to define a list of patterns (Ant-style) which match classes that should be ignored in the analysis.
It is often used to exclude commonly used classes like from the JDK, but also logging frameworks or other common libraries, or utility classes used througout the project. - includes
-
If this is defined, only explicitly included file name patterns (Ant-style) which match classes to be included. Everything else will be excluded. If not defined, everything — except the explicitly excluded files (see keyword
excludes
) — will be included. - pathSeparator
-
If defined, this String is used for the matching patterns in
api
,impl
,includes
, andexcludes
clauses.
2.3.2. Code Mapping
As of QAvalidator 1.0, code mapping is done using QAvalidator’s flavor of AOP Pointcut syntax. It works like this:
-
"*" is one package name, i.e. any number of characters except a "."
-
"**" is one or more package names, i.e. any number of characters including a "."
-
"." in the pattern is a dot, it’s not "any character" as in regular expressions.
This is very similar to Ant path matching (in fact, that’s the implementation we use; with the "." as default path
separator, or the separator defined with the keyword pathSeparator
).
QAvalidator keeps the order of definition in the architecture file. Whenever it matches a class name to an API or implementation, it starts at the top, and goes down to the bottom. The first match is the result.
2.4. Rules
QAvalidator uses the architecture description to check the dependencies found in the Bytecode.
2.4.1. QAvalidator Rules
QAvalidator checks that each dependency in the architecture view is covered by one of the following rules:
-
Is the target of the dependency an API or an implementation of a component? If it’s the latter, QAvalidator currently issues a warning. This may be changed to raising an error as soon as QAvalidator allows to define expected accessors to implementations.
-
Is the dependency allowed because the accessed component lies in the same namespace as the using component? Note that access to nested components is not allowed, only directly visible components may be used. In the example above,
Monitor
may accessIMail
because it’s on the same level, but notMailSender
because it’s a nested component ofMail
and may be an implementation detail. The architect may allow the access be using theuses
keyword. -
Is the dependency explicitly allowed via the
uses
keyword.
Compare the code in the package de.qaware.qav.core.architecture.checker
:
The class ArchitectureChecker
combines the checks of DependencyChecker
and AllComponentsImplementedChecker
.
2.4.2. Nested Components and Namespaces
The next figure shows an example for a system "S", with a number of hierarchically nested components, called "A", "A1", etc. The arrows indicate which dependencies are allowed and which are forbidden by the default rules.
-
1: A → B: is allowed, because they have the same parent.
-
2: A3 → B1: is forbidden, because they have no common parent. B1 is nested too deeply.
-
3: A22 → A3: is allowed, because A22 is a child of A2, and A2 and A3 have the same parent.
-
4: A1 → A21: is forbidden, because A21 is a child of A2. It’s nested too deeply; its existence is (or at least: may be) an implementation detail of component A2. Therefore, access from A1 is not allowed implicitly.
-
5: A22 → B1: same reasoning as number 4.
It’s really important to understand the difference in reasoning of number 3 and number 4. The essence is that the existence of child components as well as their separation of concerns may be a design or implementation decision of the parent component. In the example above, the components A21 and A22 could be collapsed and subsumed into A2. Therefore the access from outside components is not allowed by default.
Let’s now consider another example which shows the effect of the "uses" keyword in the next figure.
Now, the relations 2: A3 → B1 and 5: A22 → B1 are allowed, because the dependency A → B1 is allowed explicitly by the uses keyword. Note that allowing a dependency with the "uses" keyword also allows dependencies from children of the source of the dependency.
2.4.3. Related Work
This section compares the QAvalidator architecture rules against the rules postulated in [AP11].
[AP11] defines the following architecture rules:
- A) Dependencies are only allowed towards interfaces
-
This postulation doesn’t allow for factories and constructions like Spring 4 configurations. QAvalidator issues a warning.
- B) Only dependencies to interfaces in the same namespace are allowed
-
QAvalidator works slightly different: Dependencies in the same namespace are allowed by default. It’s possible to explicitly allow dependencies which are "further away" by using the
uses
keyword. - C) Each component and interface element of a component model has to be reflected to code
-
There is a strict mapping from code to architecture components. There is also a check that each component has corresponding classes in the code base.
- D) Relations between component model elements (components and interfaces) and code elements (types) must be 1-to-N relationships
-
There is a strict mapping from code to architecture components.
- E) Any existing architecture-level dependency must be represented by at least one code-level dependency (no absence)
-
All explicitly allowed dependency (via the
uses
keyword) are checked against the actual code base.
3. Analysis DSL
A QAvalidator analysis is composed of these types of operations which can be combined using the QAvalidator Analysis DSL:
-
Read Input
-
Create architecture views on a graph (full graph or filtered graph)
-
Manipulation
-
Validation, according to specified rules
-
Export and Visualization
This chapter gives a detailed definition of the QAvalidator Analysis DSL.
3.1. Example
Before diving in, here is an example of an analysis DSL.
It is commented inline to explain what’s going on. Based on this working example, the reference part for the analysis DSL should be easier to read.
import de.qaware.qav.graph.api.DependencyType
apply "de.qaware.qav.analysis.plugins.ShortcutQavPlugin"
// note: here are no inputJava and outuptDir statements. Use this file from within a
// Maven build, and those values will be set automatically.
analysis("Step 1: Analyze Package Architecture") {
// Prepare input: analyze the graph and create an architecture tree, based on
// the package hierarchy:
// packageGraph is now a filtered graph which only contains those nodes which
// belong to the package architecture (i.e. it does not contain the class nodes).
def packageGraph = createPackageArchitectureView(inputClassesGraph)
def packageCycleGraph = findCycles(packageGraph, "Package")
// output:
printNodes(packageCycleGraph, "packageCycleNodes.txt")
writeDot(packageGraph, "packageGraph", architecture("Package"))
}
analysis("Step 2: Prepare T-View Architecture") {
// Read the given Architecture DSL file. The architecture will be available
// under the name defined in the Architecture DSL file; it can be accessed
// with: architecture("T-View")
readArchitecture "${systemName}_architecture.groovy"
// Use that architecture and apply it on the dependency graph.
// Do so twice: once for the full graph, and once only on the part of graph
// which represents only classes from the input scope (leaving out referenced
// 3rd-party classes).
architectureTView = createArchitectureView(allClassesGraph, architecture("T-View"))
architectureTViewOnInput = createArchitectureView(
inputClassesGraph, architecture("T-View"), "T-View-on-Input")
}
analysis("Step 3: Analyze and check for violations") {
// Check all architecture rules: all relations must be covered in the architecture
// definition, all components must actually be implemented, and all rules in the
// architecture file are really used.
checkArchitectureRules(architectureTView, architecture("T-View"))
// Find cycles on the component level
architectureTViewCycleGraph = findCycles(architectureTView, "T-View")
}
analysis("Step 4: Export as DOT, GraphML, and JSON") {
// graphical export as DOT (for GraphViz) and GraphML (for yEd)
writeDot(architectureTView, "architectureTView", architecture("T-View"))
writeDot(architectureTViewCycleGraph, "architectureTViewCycleGraph",
architecture("T-View"))
writeDot(architectureTViewOnInput, "architectureTViewOnInput",
architecture("T-View"))
// this is to import it into qav-server for interactive exploration of the
// dependency graph
writeFile(dependencyGraph, "dependencyGraph.json")
}
3.2. Overview and DSL Plugins
The QAvalidator Analysis DSL a Groovy DSL. So valid Groovy code may be used anywhere.
Only some core concepts are implemented with a few hard-coded elements. The heavy lifting is done in plugins. These are the core concepts and the corresponding language features:
3.2.1. Analysis Steps
Analysis is done in steps.
analysis
QAvalidator will execute all analysis
blocks in the order of their appearance.
It will print their duration and their success.
Analysis steps will fail if they detect a violations
, or if there are unexpected errors (aka Exceptions).
An analysis run which has at least one failed analysis step will be considered as failed.
| Parameter | Description |
|---|---|
description |
Accepts a brief description of the analysis block |
closure |
code to execute in that analysis |
3.2.2. Error handling
QAvalidator distinguishes between error and violation (this is similar to a unit test which may be ok, produce an error, or may have failed).
error
Throws a new IllegalStateException
with the given message.
This is meant for technical error which should not happen in the course of the analysis. QAvalidator will log the message and the full stack trace to help debugging the analysis script.
| Parameter | Description |
|---|---|
message |
The message, to throw as |
violation
Accepts a message (a String
), and notes it in the context
(see next section).
This is meant for architecture violations which are detected by the QAvalidator analysis run. This is not an error (the analysis run could do all its work), but it’s not a success (as the analyzed system contains architectural flaws). QAvalidator will log the messages, and mark the analysis step as failed. See section Failing the Build.
| Parameter | Description |
|---|---|
message |
The message to describe the violation. |
3.2.3. Common data
The analysis script may use the context
to store objects, or to pass objects from one step to another.
-
QAvalidator uses the context to create the initial graph (
context.dependencyGraph
). -
The
JavaQavPlugin
uses this graph and creates a node for each class, and an edge for each dependency of every class. All other plugins use this graph to read it, and to work with it. -
The
ShortcutQavPlugin
also makes heavy use of this feature, and provides prepared data and tools like filters in the context.
3.2.4. Plugins
Analysis is implemented in plugins
apply
Applying a plugin makes the language features of that plugin available to the analysis script.
The plugins offer a number of features used in many analysis. So typically, they will be used to compose new analysis. If necessary, new plugins can be added for other or more specific jobs.
| Parameter | Description |
|---|---|
plugin |
Accepts an object which must implement the interface |
3.3. Input Scope
QAvalidator works on input scopes which define the system to analyze.
As of version 1.0, QAvalidator can read Java (.class
files) and Maven POMs as input.
For all upcoming analysis steps, it is not relevant where the initial graph comes from. From then on, QAvalidator uses
nodes and edges, attributes on them (like types) etc. for its analysis.
All analysis is language-agnostic; apart from the JavaQavPlugin
which reads .class
files, there is nothing
Java-specific in QAvalidator.
One feature is that new languages may be added by implementing a new input scope reader. More important is the fact that the architecture model employed by QAvalidator is not language-specific.
3.3.1. JavaQavPlugin
Provides the Java input for QAvalidator
inputJava
Read all "*.class"
files under the given directories, and add them to the
DependencyGraph
which must exist in the context.
Lets the user decide if he wants to collapse inner classes.
| Parameter | Description |
|---|---|
parameters |
Accepts a map with This command may be called multiple times. The directory names are added (not replaced). For each call, it is possible to tell whether inner classes should be collapsed, i.e. folded onto the outer class, or not. The |
collapseInnerClasses |
Defaults to true. |
inputJava
Read all "*.class"
files under the given directories, and add them to the
DependencyGraph
which must exist in the context.
Collapses inner classes, i.e. folds inner classes onto the outer class; all class-level
relationships will be handled as if they would affect the outer class.
| Parameter | Description |
|---|---|
classesRootDirNames |
Accepts one or more directories where it recursively searches all This command may be called multiple times. The directory names are added (not replaced). The |
inputJava
Read all "*.class"
files under the given directories, and add them to the
DependencyGraph
which must exist in the context.
Lets the user decide if he wants to collapse inner classes.
| Parameter | Description |
|---|---|
collapseInnerClasses |
|
classesRootDirNames |
Accepts one or more directories where it recursively searches all This command may be called multiple times. The directory names are added (not replaced). For each call, it is possible to tell whether inner classes should be collapsed, i.e. folded onto the outer class, or not. The |
3.4. Creating and Working with Architecture Views on a Graph
An architecture view is a hierarchy of components above a certain input. It is a tree where the inner nodes are architecture components, and the leaves are the elements of the input scope (see section Input Scope).
There may be an arbitrary number of architecture views on an input graph. Different architecture views are independent from each other. See section Definitions for a definition of an "architecture view".
3.4.1. ArchitectureQavPlugin
Operations offered by the ArchitectureQavPlugin:
-
Read an Architecture:
-
Read an Architecture DSL file
-
Use the Package Structure as Architecture View
-
-
Working with Architecture Views
-
Create an Architecture View
addArchitecture
Add an architecture. This command is useful for other plugins; it will probably not be used in Analysis DSL scripts.
| Parameter | Description |
|---|---|
architecture |
the |
architecture
QAvalidator stores all Architectures centrally under a unique name. For an architecture that is defined using the DSL, that name is defined in the DSL. For an architecture derived from the actual package hierarchy, it is "Package".
This command returns the Architecture
registered under the given name,
or null if no architecture is registered under that name.
| Parameter | Description |
|---|---|
name |
The name. |
- Result
-
The
Architecture
or null.
createArchitectureView
Creates a new architecture view in the given DependencyGraph
.
The result is a DependencyGraph
which is a filtered version of the incoming sourceGraph
.
| Parameter | Description |
|---|---|
sourceGraph |
the graph from which we create the new architecture view. |
architecture |
the |
tag |
The tag to write on every node on the given sourceGraph and on each parent architecture node. This parameter is optional: It defaults to the architecture name. |
- Result
-
A filtered graph which contains all nodes which belong to this new architecture.
createPackageArchitectureView
Uses the package structure to create a new Architecture
based on the package names.
That package architecture is available to the Analysis script under the name Package
, i.e. by
using architecture("Package")
.
If the parameter maxDepth
is given and greater than 0, the package names will have maxDepth
levels at most; the name of the architecture is Package-n
with n being maxDepth
.
Then, this command uses createArchitectureView
to create the architecture view, and returns the
filtered graph.
| Parameter | Description |
|---|---|
graph |
the |
maxDepth |
the maximum depth of the package names. Defaults to |
- Result
-
A filtered graph which contains all nodes which belong to this new architecture.
createPackageArchitectureView
Create a new Architecture
based on the item names.
The parameter separator
defines how to split the names.
That architecture is available to the Analysis script under the given name, i.e. by using e.g. architecture("Package")
.
If the parameter maxDepth
is given and greater than 0, the names will have maxDepth
levels at
most; the name of the architecture is Name-n
with n being maxDepth
.
Then, this command uses createArchitectureView
to create the architecture view, and returns the
filtered graph.
This command is useful to create an Architecture View e.g. based on the Package names of the classes.
| Parameter | Description |
|---|---|
graph |
the |
architectureName |
the name of the architecture. |
separator |
the String to split and join the names again. |
maxDepth |
the maximum depth of the package names. Defaults to |
- Result
-
A filtered graph which contains all nodes which belong to this new architecture.
mapDependencies
Maps all dependencies in the given DependencyGraph
one level "up" in the given Architecture
,
and returns the given graph, filtered so that it only contains the architecture nodes, but not the base nodes.
So this command helps to reduce a given DependencyGraph
.
| Parameter | Description |
|---|---|
dependencyGraph |
the |
architecture |
the |
- Result
-
The result is a
DependencyGraph
representing the given Architecture; the graph contains the dependencies on its lowest level, i.e. on the leaves of the architecture tree.
readArchitecture
Reads an Architecture
from the given file name and registers it under the name defined in the file.
The architecture is then available using the architecture(<name>)
command.
| Parameter | Description |
|---|---|
filename |
The filename. It can refer to a file in the file system or in the classpath, using the
prefix If there the path is relative, then QAvalidator also searches for the architecture DSL file in the same place as the analysis DSL file. This is useful if the analysis file and the architecture file live in the same source tree. |
3.5. Manipulation
3.5.1. GraphFilterQavPlugin
A graph can be filtered according to properties on the nodes or on the dependencies. Filtered graphs can then be used for further analysis (see Validation), or they can be exported for visualization (see Export and Visualization).
The GraphFilterQavPlugin offers are a number of filters on nodes and on edges.
Filters can be combined in the expected ways, using combinations with and
and or
, and negation with not
.
addFilter
Registers a new filter under the given name.
| Parameter | Description |
|---|---|
name |
the name |
filter |
the filter |
- Result
-
Returns the filter.
and
Creates a new AndFilter
.
| Parameter | Description |
|---|---|
filters… |
The filters to combine with AND |
- Result
-
A new
AndFilter
dependencyPropertyExistsFilter
Creates a new DependencyPropertyExistsFilter
to filter which dependencies to keep.
| Parameter | Description |
|---|---|
propertyName |
The property name which must be set on the dependency. The value does not matter. |
- Result
-
The new
DependencyPropertyExistsFilter
.
dependencyPropertyInFilter
Creates a new DependencyPropertyInFilter
to filter which dependencies to keep.
| Parameter | Description |
|---|---|
propertyName |
The property name |
value |
The value that must be there to accept the edge. |
- Result
-
The new
DependencyPropertyInFilter
.
dependencyToFilter
Creates a new DependencyToFilter
.
Accepts all edges which have target nodes accepted by the given base filter.
| Parameter | Description |
|---|---|
baseFilter |
the node filter for target nodes |
- Result
-
The new
DependencyToFilter
dependencyTypeEdgeOutFilter
Creates a new DependencyTypeEdgeOutFilter
to filter out dependencies.
| Parameter | Description |
|---|---|
dependencyTypeNames… |
The dependency type names to filter out. |
- Result
-
The new
DependencyTypeEdgeOutFilter
.
dependencyTypeEdgeOutFilter
Creates a new DependencyTypeEdgeOutFilter
to filter out dependencies.
| Parameter | Description |
|---|---|
dependencyTypes… |
The dependency types to filter out. |
- Result
-
The new
DependencyTypeEdgeOutFilter
.
filter
Returns the filter with the given name.
| Parameter | Description |
|---|---|
name |
the name |
- Result
-
The filter registered under that name, or null if no such filter is available.
nodeHasDependencyToFilter
Creates a new NodeHasDependencyToFilter
. Accepts all nodes which have outgoing dependencies to
nodes which are accepted by the given base filter.
| Parameter | Description |
|---|---|
graph |
the graph to work on |
baseFilter |
the node filter |
- Result
-
the new
NodeHasDependencyToFilter
nodeHasIncomingDependencyTypeFilter
Creates a new NodeHasIncomingDependencyTypeFilter
. This is an IN filter.
| Parameter | Description |
|---|---|
dependencyTypeName |
The dependency type name to accept in the filter. |
- Result
-
The new
NodeHasIncomingDependencyTypeFilter
.
nodeHasIncomingDependencyTypeFilter
Creates a new NodeHasIncomingDependencyTypeFilter
. This is an IN filter.
| Parameter | Description |
|---|---|
dependencyType |
The dependency type to accept in the filter. |
- Result
-
The new
NodeHasIncomingDependencyTypeFilter
.
nodeHasOutgoingDependencyTypeFilter
Creates a new NodeHasOutgoingDependencyTypeFilter
. This is an IN filter.
| Parameter | Description |
|---|---|
dependencyTypeName |
The dependency type name to accept in the filter. |
- Result
-
The new
NodeHasOutgoingDependencyTypeFilter
.
nodeHasOutgoingDependencyTypeFilter
Creates a new NodeHasOutgoingDependencyTypeFilter
. This is an IN filter.
| Parameter | Description |
|---|---|
dependencyType |
The dependency type to accept in the filter. |
- Result
-
The new
NodeHasOutgoingDependencyTypeFilter
.
nodeNameInFilter
Creates a new NodeNameInFilter
.
| Parameter | Description |
|---|---|
patterns… |
The patterns (Ant path style) to accept. If any of the given patterns matches the name, the node is accepted. |
- Result
-
The new
NodeNameInFilter
.
nodeNameOutFilter
Creates a new NodeNameOutFilter
.
| Parameter | Description |
|---|---|
patterns… |
The patterns (Ant path style) to filter out. Only if none of the given patterns matches the name, the node is accepted. |
- Result
-
The new
NodeNameOutFilter
.
nodePropertyExistsFilter
Creates a new NodePropertyExistsFilter
. This is an IN filter.
| Parameter | Description |
|---|---|
propertyName |
The name of the property which must exist to accept the node. |
- Result
-
The new
NodePropertyExistsFilter
.
nodePropertyInFilter
Creates a new NodePropertyInFilter
.
| Parameter | Description |
|---|---|
propertyName |
The property name |
value |
The value that must match to accept the node. |
- Result
-
The new
NodePropertyInFilter
.
3.6. Validation
3.6.1. AnalysisQavPlugin
QAvalidator’s main mission is to validate source code against intended architecture concepts. If a validation fails, QAvalidator can either issue a warning, or it can fail the build.
checkArchitectureRules
Validate against a specific architecture. Checks the given graph according to the given architecture. It checks these rules:
-
Checks that all edges are allowed, i.e. that for each edge there is at least one rule which justifies the existence of that edge
-
Checks that all components are actually implemented. I.e. we don’t want to have components which are defined on the architecture level, but don’t have corresponding classes in the code base.
-
Checks that all rules which are explicitly defined are used in the code base.
For each rule which is not fulfilled, the command issues an error and writes a line into the SonarQube error file for the SonarQube analysis.
| Parameter | Description |
|---|---|
architectureGraph |
the graph where we want to find rule violations |
architecture |
Architecture to check against |
checkDependencyRules
Validate against a specific architecture. Checks the given graph according to the given architecture. It checks this rule:
-
Checks that all edges are allowed, i.e. that for each edge there is at least one rule which justifies the existence of that edge.
If the rule is not fulfilled, the command issues an error and writes a line into the SonarQube error file for the SonarQube analysis.
| Parameter | Description |
|---|---|
architectureGraph |
the graph where we want to find rule violations |
architecture |
Architecture to check against |
findCycles
Checks the input graph for cycles.
| Parameter | Description |
|---|---|
graph |
the DependencyGraph to find the cycles in |
scope |
name of the scope, used for error messages |
filterContains |
if |
- Result
-
The given graph, filtered to only those nodes which are part of a cycle. I.e., if the resulting graph is empty, then the original graph is free of cycles. If it is not free of cycles, QAvalidator issues a violation and marks the analysis step as failed.
findDependenciesTo
| This is an incubating feature, introduced in 1.2.7. It may change without further notice. |
Finds all nodes which have outgoing dependencies to nodes which match one of the given patterns. If it finds any, it reports a violation.
Returns a filtered graph which contains only the nodes specified by the patterns and those with dependencies to them.
| Parameter | Description |
|---|---|
graph |
the DependencyGraph to analyze |
patterns |
list of target name patterns |
- Result
-
a filtered graph which contains only the nodes specified by the patterns and those with dependencies to them.
3.7. Export and Visualization
3.7.1. IOQavPlugin
QAvalidator language elements for Input/Output, reading and writing JSON, DOT, or text-format Node infos
abbreviation
Registers a new abbreviation which will be used for DOT exports.
| Parameter | Description |
|---|---|
longName |
the long name to abbreviate |
shortName |
the abbreviation |
outputDir
Sets the output directory. If the "clean" flag is set to true, deletes the directory (and its content) and then re-creates it.
Throws an IllegalArgumentException
if there exists a file (not a directory) with the
given name, or if the directory can’t be created (as indicated by an IOException).
Note that QAvalidator only allows to set the outputDir
once. This is helpful if the analysis
DSL file defines an output directory, but the command line or the Maven pom overrides that
directory.
| Parameter | Description |
|---|---|
path |
the path |
clean |
if true, deletes the directory completely before creating it. Defaults to |
printNodes
Prints the given node of the given graph into a file.
| Use filters to print only the relevant nodes and edges. |
| Parameter | Description |
|---|---|
dependencyGraph |
The graph in which the nodes lie. |
filename |
The filename; it is relative to the |
printIfEmpty |
if |
readFile
Read the graph from the given file.
| Parameter | Description |
|---|---|
filename |
the filename |
- Result
-
the new graph
readFile
Read the graph from the given file, and merges it into the given graph.
| Parameter | Description |
|---|---|
parameters |
Accepts a map with a |
- Result
-
the new graph
writeDot
Writes the given graph to a DOT (GraphViz) file with the given filename in the directory defined as outputDir.
Then calls dot (GraphViz) to create .png and .svng files from it. Also writes a GraphML file with the same base filename, to be used with yEd.
| Install GraphViz to allow for .png and .svg creation. |
| Parameter | Description |
|---|---|
dependencyGraph |
the graph |
filename |
the filename |
architecture |
the |
createEdgeLabels |
The edge labels give the the number of base relations (at the middle of an edge), the number of different base relation sources (at the start of an edge), and the number of different base relation targets (ad the end of an edge). This parameter is optional. It defaults to |
writeFile
Writes the given graph to a file with the given filename in the directory defined as outputDir.
| Parameter | Description |
|---|---|
dependencyGraph |
the graph |
filename |
the filename |
writeGraphLegend
Writes the graph legend with the given filename base in the directory defined as outputDir. Then calls dot (GraphViz) to create a .png file from it. Also writes a GraphML file with the same base filename, to be used with yEd.
This is useful to see the color code for the dependency types.
| Parameter | Description |
|---|---|
filenameBase |
the filename base, relative to outputDir; defaults to |
writeNeo4j
Exports the given graph to a Neo4j DB. This database is accessed using the BOLT protocol; i.e. the
Neo4j server must be running and must be accessible. The URI and credentials must be provided in the
environment variables NEO4J_URI
, NEO4J_USERNAME
, NEO4J_PASSWORD
.
| Parameter | Description |
|---|---|
dependencyGraph |
the graph to export |
clearAll |
if true, delete all nodes in the DB. Defaults to false |
3.8. SonarQube and Monitoring the Quality Contract
3.8.1. SonarMessagesQavPlugin
QAvalidator may be used as part of a SonarQube analysis (see [SonarQube]).
It writes a log file for SonarQube; each architecture violation is written on one line.
A SonarQube Plugin can read that file and report the number of violation as part of the SonarQube
Quality Contract.
The file is located in the directory defined as outputDir
(via command line, or in the Maven POM,
or in the analysis file).
3.9. Typical Analysis Steps
3.9.1. ShortcutQavPlugin
The plugin ShortcutQavPlugin
combines the most common preparations, and registers all of the above listed plugins.
-
It registers other plugins (
apply
) -
It define filters: ignoreUtils, classes, modules, inputScope, mavenScope, cycleFilter
-
It defines filtered Graphs:
-
allClassesGraph: all nodes in
context.dependencyGraph
which represent a class, i.e. it filters all nodes:-
Property "type" has value "class": it filters out all nodes which represent "component" instead of "class".
-
Property "name" not in
"java.lang.xxx"
,"java.util.xxx"
,"java.io.xxx"
,"org.slf4j.xxx"
: No util classes.
-
-
inputClassesGraph: all of
allClassesGraph
, but also:-
Property "scope" has value "input": Only the classes in the input scope, filter out all referenced classes which are not part of the input scope
-
-
3.10. Failing the Build
If QAvalidator is used via the Maven plugin, it can be configured to break the build if validations fail, or if serious errors occur.
|
Technically speaking, this is the case when an Exception is thrown. This happens for
|
4. UI
QAvalidator offers a web UI to browse a dependency graph.
4.1. Start the Web UI
4.1.1. Start via Command Line
Start the server from the command line. It needs the graph file as parameter (mandatory).
$ java -jar qav-app-<version>.jar --graph=path/to/dependencyGraph.json \
[--server.port=8080]
By default, the server will start on port 8080. With --server.port=xx
, another port can be assigned.
4.1.2. Start via Maven Plugin
Alternatively, start it via the Maven build. Add another execution
block to the plugin configuration. The configuration block accepts two more parameters, graph
for the graph file to read, and port
for the server port to use. The example shows the default values.
<executions>
<execution>
<id>server</id>
<goals>
<goal>server</goal>
</goals>
</execution>
</executions>
<configuration>
<graph>target/qav-report/dependencyGraph.json</graph>
<port>8080</port>
</configuration>
Alternatively, define the properties on the command line; then no specific configuration is required in the pom.xml
:
mvn qav:server -Dserver.port=8080 -Dserver.graph=target/qav-report/dependencyGraph.json
The QAvalidator Server will read the dependencyGraph.json
file, index it with an in-memory Lucene index to allow for a flexible search, and offer a plain web UI to browse the graph.
Stop the server with Ctrl-C
.
4.1.3. Start via Gradle Plugin
You can start the Server via the Gradle plugin. The configuration accepts two more parameters; the example shows the default values.
qavalidator {
port = '8080'
graph = 'build/qav-report/dependencyGraph.json'
}
Start the server with this command:
./gradlew qavalidatorServer
and stop it with Ctrl-C
.
4.2. Search
The main page shows a search dialog.
If no query is entered, it lists all nodes.
A query uses the Lucene query syntax. To find a node with name "de.qaware.qav", the search query is:
name:de.qaware.qav
The result will be the node with the name "de.qaware.qav".
To find all nodes starting with "de.qaware.qav", the search query is:
name:de.qaware.qav*
The result will be a list of nodes.
If searching for a name, the prefix name:
may be omitted. I.e. searching for de.qaware.qav*
will yield the same results.
To find all nodes which are architecture nodes (i.e. not class-level nodes), the query is:
type:architecture
To find all nodes which belong to the input scope (i.e. were read initially, as opposed to classes which are referenced by classes in the input scope):
scope:input
4.3. The node detail view
Selecting a class navigates to the node detail view.
It shows:
-
Node properties
-
The hierarchy: this is "vertical navigation" in the graph
-
Incomonig and outgoing dependencies: this is "horizontal navigation in the graph.
Clicking on a node navigates to that node’s detail views.
4.4. The dependency detail view
Clicking on the dependency type in the incoming and outgoing dependencies part navigates to the dependency detail view.
It shows:
-
Source, target, and type: for better overview and easy navigation to the source and target nodes.
-
Edge properties. One property, if available, is
lineNo
; it tells the line number in the source file where the class of the target node is referenced. -
Base dependencies, if applicable: For dependencies on architecture level, the underlying dependencies (the base dependencies) are shown. Again, the "Source" and "Target" nodes are links to their node detail pages, whereas the "Dependency Type" is a link to the edge detail page.
4.5. The images view
The menu item "Images" opens a dialog displaying a list of all images of that analysis run. It offers to show the detail page with a PNG or an SVG image, as well as a download link of the GraphML file.
The detail view shows the image; there is a button to toggle between a downscaled size (which fits the screen) and a 100% size of the image.
| Right-clicking on the image and "View image" opens the image in full size, and allows to use the links embedded in the image. This allows to navigate directly to the node detail view. |
5. Developer Documentation
This is the documentation for version 1.2.15.
The intended audience of this chapter are developers who want to enhance or contribute to QAvalidator.
5.1. QAvalidator Core Ideas
-
The analysis is defined in the QAvalidator Analysis DSL.
-
Architecture definitions are defined using the QAvalidator Architecture DSL.
-
The QAvalidator Core is more like an executer framework for analysis steps.
-
Analysis is done in plugins. QAvalidator comes with a number of plugins. New plugins can be easily defined. So if there is functionality missing, it’s often a good idea to write a new plugin.
-
5.1.1. DependencyGraph
To understand the QAvalidator code, the most important data structure is the DependencyGraph
.
-
The input scope is read and put into the
DependencyGraph
. -
Any architecture view is a tree. It is put into the same
DependencyGraph
as the input scope. The nodes are differentiated by thetype
attribute. -
This graph can be filtered by attributes on the nodes or on the edges.
-
A graph (the original or a filtered graph) can be used for further analysis steps, or it can be e.g. exported to a
*.dot
or*.graphml
file, or to our own*.json
format. It can be analyzed, e.g. it can be checked for cycles.
5.1.2. Defining the Analysis DSL and Running an Analysis
The class QavAnalysisDSL
is the base class for analysis DSL scripts. I.e. this class defines the vocabulary of the analysis DSL. It does so in two ways: Methods defined in this class are available to every analysis DSL script. And it allows to register plugins, which can add more methods and dynamically enhance the analysis DSL.
The QavAnalysisReader
reads a QAvalidator Analysis DSL file, and triggers the analysis.
The QavAnalysisRunner
runs all analysis steps.
5.2. Project Structure
The project is organized as shown in the next figure.
From bottom to top, these are the modules:
-
Utilities:
-
qav-doc-generator: Generates the QAvalidator Analysis DSL documentation; this is done using annotation processing at compile time.
-
qav-util: Code that is used both by most parts of QAvalidator; utilities to work with strings or files etc.
-
-
Documentation:
-
qav-doc: This documentation.
-
-
Main Model:
-
qav-graph: This module contains the
DependencyGraph
, which is the core data model that QAvalidator works with.
-
-
Processing:
-
Input:
-
qav-input-java: reads Java
.class
files and.jar
files. -
qav-input: reads Maven
pom.xml
files, input produced by the TypeScript analyzer, and Zipkin Traces.
-
-
Transformation & Validation:
-
qav-architecture-dsl: This is the data model for representing an
Architecture
. Also contains the DSL to read an architecture definition from a QAvalidator Architecture DSL file. -
qav-architecture: Contains factories, validators, and transformations to work with architectures, architecture components, and architecture sub-graphs.
-
-
Output:
-
qav-visualization: Exporters to
.dot
(for GraphViz) and to.graphml
(for yEd). -
qav-graph-db: Exports the
DependencyGraph
to Neo4j.
-
-
Analysis: DSL & Orchestration:
-
qav-analysis-dsl: This is the DSL to define analysis runs, and to compose analysis steps.
-
qav-analysis-plugins: Contains the plugins which define language constructs of the QAvalidator Analysis DSL.
-
The former is the technical framework to set up the DSL, and the latter is the actual commands; they use the steps provided in input, transformation & validation, and output.
-
qav-runner: provides an API to start analysis runs. I.e. it combines everything so that the following QAvalidator start options are small and easy to implement.
-
-
-
Distribution:
-
qav-gradle-plugin: start QAvalidator from a Gradle run.
-
qav-maven-plugin: start QAvalidator from a Maven run.
-
qav-app: start QAvalidator standalone, from command line, including a web application to view graphs.
-
qav-ui: The Angular-2 web app, served via qav-app.
-
-
Result Reading:
-
qav-sonar-plugin: The SonarQube plugin which reads
qav-sonar.log
files and offers the results as metrics.
-
6. Release Notes
6.1. Release 1.2.15 — (01.07.2019)
- Breaking Changes
-
-
Architecture DSL: if a pattern ends with
.*
, QAvalidator had a compatibility rule and used to interpret that as.**
. This version of QAvalidator removes this legacy rule.
-
- Changes
-
-
Upgrade libraries, both in the UI and in the Java backend.
-
Fix a bug in the Neo4j exporter to handle method nodes correctly.
-
6.2. Release 1.2.14 — 28.03.2019
- Breaking Changes
-
-
none.
-
- New Features
-
-
#8: Add nodes and dependencies on method level.
-
6.5. Release 1.2.11 — 12.02.2019
- Breaking Changes
-
-
none.
-
- New Features
-
-
#17: Add images to the Web UI. All results of a QAvalidator run can now be viewed in the Web UI. And it’s possible to navigate from the image to the detail view: The SVG images contain links to the respective pages.
-
#16: Enhance the output of the default analysis.
-
#14: Java Input reader: Add support for newer Java versions
-
6.6. Release 1.2.10 — 18.01.2019
- Breaking Changes
-
-
removed deprecated commands:
-
reportLeftOvers
: was deprecated in 1.2.0 -
printNodes
: removed variant which was deprecated in 1.2.0
-
-
- New Features
- Changes
-
-
Library upgrades, UI upgrade.
-
Releases 1.2.8 and 1.2.9: Skipped.
6.7. Release 1.2.7 — 31.01.2018
- Breaking Changes
-
-
renamed some filters in
GraphFilterPlugin
, to have more consistent naming of the filter commands:-
(old)
dependencyTypeNodeFilter
→ (new)nodeHasIncomingDependencyTypeFilter
.
This filter’s scope is also reduced to incoming dependency types; use an or filter to combine it with outgoing dependencies, if required. -
(old)
outgoingDependencyTypeFilter
→ (new)nodeHasOutgoingDependencyTypeFilter
. Only the name changed. -
(old)
propertyExistsFilter
→ (new)nodePropertyExistsFilter
. Only the name changed. -
(old)
propertyInFilter
→ (new)nodePropertyInFilter
. Only the name changed.
-
-
- New Features
-
-
#3: new command
writeGraphLegend
writes a legend (key) for the graphs exported withwriteDot
. This is useful to see the color code for the dependency types. Also change the colors of the arrows to a more coherent color set. -
#4: introduced new keyword in the Architecture DSL
usesImpl
to allow a dependency on an implementation part instead of an API. This is useful to model factories or Spring 4 configurations (or other exceptions from the rule, if there is a really good excuse). -
#5: introduced new command
findDependenciesTo
: Finds all nodes which have outgoing dependencies to nodes which match one of the given patterns. If it finds any, it reports a violation. -
New filters:
-
dependencyToFilter
: Accepts all edges which have target nodes accepted by the given base filter. -
nodeNameInFilter
: accepts all nodes which match one of the given patterns -
nodeHasDependencyToFilter
: accepts all nodes which have outgoing dependencies to nodes accepted by the given base filter
-
-
6.8. Release 1.2.6 — 04.11.2017
- Changes
-
-
Changed the way to start the QAvalidator Server for the Web UI:
-
The server may be started via Maven Plugin or Gradle Plugin. Refer to chapter UI for details on the configuration.
-
The stand-alone app now also starts the server. So use
qav-app
instead ofqav-server
. -
There is no standalone
qav-server
executable JAR file anymore.
-
-
Minor changes to the Maven plugin configuration. See the documentation for details.
-
6.10. Release 1.2.4 — 11.09.2017
- Changes
-
-
Dependencies:
-
Set the minimum required SonarQube version to 5.6.3
-
Spring Boot: 1.4.2 → 1.5.6
-
Spring: 4.3.4 → 4.3.10
-
Spring Data: 1.12.5 → 1.13.6
-
Jackson: 2.8.7 → 2.8.9
-
Slf4j: 1.7.21 → 1.7.25
-
Logback: 1.1.7 → 1.1.11
-
-
Refactoring: Split the code base into way more Gradle modules. See Project Structure for an up-to-date description of the code structure. There should not be any changes visible to QAvalidator users.
-
Release via Bintray to make QAvalidator publicly available.
-
- Deprecations
-
-
Analysis DSL:
-
The ShortcutQavPlugin moved from
de.qaware.qav.core.analysis.dsl.plugins.ShortcutQavPlugin
tode.qaware.qav.analysis.plugins.ShortcutQavPlugin
. Replace theapply
statement in your analysis files accordingly.
-
-
Architecture DSL / class matching expressions:
-
If "*" is the last package names, it’s handled like "**". This will be removed in one of the next releases, so change your Architecture definition and replace
my.prefix.*
bymy.prefix.**
-
-
6.11. Release 1.2.3 — 25.07.2017
First release which is published via Bintray.
- Breaking Changes
-
-
Change
NodeNameOutFilter
to accept Ant style path matcher instead of regular expressions. Note that this might be a breaking change. -
There was a refactoring: if there are import statements like this in analysis files, then replace
import de.qaware.qav.core.graph.api.DependencyType
byimport de.qaware.qav.graph.api.DependencyType
-
- Changes
-
-
Fixed a bug; now dependencies to APIs (not only to components) can be declared as explained in the documentation.
-
6.12. Release 1.2.2 — 09.03.2017
-
Fix Gradle plugin. There was a bug that it couldn’t find the architecture DSL base.
6.13. Release 1.2.1 — 06.03.2017
- Breaking changes
-
-
ignore
,includes
, andexcludes
can only be defined on the level of the Architecture — not on any other component. This is a breaking change, but makes understanding the Architecture DSL easier.
-
- Changes
-
-
The command
findCycles
now filters away CONTAINS relations. In most cases, those relations are not relevant for the search for cycles.
-
So an analysis file can be simplified:
def packageCycleGraph = findCycles(packageGraph
.filter(dependencyTypeEdgeOutFilter(CONTAINS)), "Package")
can now be:
def packageCycleGraph = findCycles(packageGraph, "Package")
If, for some reason, they really should be part of the search, there is a new optional boolean parameter to not filter away these dependencies:
def packageCycleGraph = findCycles(packageGraph
.filter(dependencyTypeEdgeOutFilter(CONTAINS)), "Package", true)
-
#28: Generalize
ignore
toincludes
/excludes
. A class will only be considered if its name is included and not excluded:-
it is included if no
includes
pattern is given or it is matched by at least one of theincludes
patterns. -
it is excluded if
excludes
patterns are given, and a least oneexcludes
pattern matches the name. -
This replaces the
ignore
command (which still works, but is deprecated in favor ofincludes
/excludes
).
-
-
#38: QAvalidator also reads class files from
.jar
,.war
, and.ear
files, and unpacks nested archives. Note that it’s necessary to give anincludes
pattern both for the archive files (otherwise they will not be opened), and for the class file entries to read.
Example:
inputJava baseDir: "build/libs/qav-app-1.2.2-SNAPSHOT.jar", (1)
includes: ["**/*.class", "**/qav-*.jar"] (2)
-
the archive file is given as input directory
-
the
includes
patterns allow for all.class
files, and for all.jar
files file which start withqav-*
in any directory within a jar. This also holds for further nested archives.
If the archive is given on the command line as non-option argument, the default behaviour is to use all .class
files, and to open all nested archive files. This may not always be the desired behaviour, as it also analyzes all the packaged third-party libraries. It will often be a good idea to filter the jar files which are unpacked.
Another option is to define a directory as baseDir
, and find one or more archive files in there:
inputJava baseDir: "build/libs", (1)
includes: ["**/de/qaware/qav/**/*.class", "**/qav-*.jar"] (2)
The advantage is that if the archive names change, the directory can be the same (1), and the archive files can be found with an includes
pattern (2). In this example, the classes in the input are restricted to match only classes under de.qaware.qav.**
.
-
#41: Visualization: add labels to the edges. The edge labels give the the number of base relations (at the middle of an edge), the number of different base relation sources (at the start of an edge), and the number of different base relation targets (ad the end of an edge). This is the new default behaviour; so e.g. this command will add labels to the edges:
writeDot(architectureTView, "architectureTView", architecture("T-View"))
If the labels are not desired, they can be switched off with a new, optional parameter like this:
writeDot(architectureTView, "architectureTView", architecture("T-View"), false)
- Deprecations
-
-
The DSL command
ignore
is now deprecated; it is replaced byexcludes
. If noincludes
is given, it behaves the same asexcludes
. See above.
-
6.14. Release 1.2.0 — 02.03.2017
Breaking changes:
-
#30, #32: Reworked tagging of nodes in architecture views.
-
Retired the
reportLeftOvers
feature, and retired the convention to have a "Rest" component. The commandcreateArchitectureView
reports unmapped classes. -
Fixed tagging of architecture components. The command
createArchitectureView
now tags all class nodes in the given graph and all of the architecture component nodes which belong to those given class with the given tag. This makes it easier to deal with the resulting graph, especially when the architecture view is created on only a subset of the full graph (e.g. only on the input scope). — However, this implies changes to the way we detect unimplemented components. -
Added new command to the analysis DSL:
checkDependencyRules
checks if every dependency in the given architecture view is covered by a rule in the architecture definition. This rule has been checked by the commandcheckArchitectureRules
— however, if the architecture view is built on only a subset of the input graph, there will be unmapped components which would (wrongly, depending on the perspective), be reported as unimplemented components. To avoid confusion, the rules may now be checked separately.
-
Changes:
-
#31: the Sonar plugin reports each cycle separately, so that the QAcontract plugin can show the number of cycles.
-
#33: Updated the Analysis DSL documentation. Improved documentation on Maven multi-module builds.
-
#34: Improved error messages.
-
#37: The default locations for the analysis filename now includes three places which are checked in this order:
qa/analysis.groovy
(as it used to be),src/qa/analysis.groovy
(new), andclasspath:/default_analysis.groovy
(as it used to be). If an analysis file name is defined in Gradle, Maven, or on the command line, that file must exist, and QAvalidator will not try its fallbacks. If no filename is specified, then QAvalidator will try the defaults. -
#39: The command
createPackageArchitectureView
now accepts an optional argument which defines the maximum depth of the package hierarchy. If this new argument is not given, it defaults to 0 which means "unlimited" and is the previous behaviour.
Fixed Bugs:
-
#36: Fixed:
JavaScopeReader
did not find references to attributes in other classes. -
Fixed a bug: dependency on implementation is now recognized correctly.
Deprecations:
-
The command
reportLeftOvers
is deprecated. See above.
6.15. Release 1.1.1 — 06.02.2017
-
#23: The release process now deploys the fat executable JARs to Nexus.
-
#24: Added documentation.
-
#24: Simplified the default analysis. It now only analyses the input scope, checks for package cycles, and produces some output; it uses only
inputClassesGraph
, no longerallClassesGraph
. -
#24: Simplified the analysis DSL. The third parameter to command
createArchitectureView
is now optional. -
#26: Fixed bug in analysis engine: finds relations to annotations on attribute level, ignores primitive types (and arrays of those)
-
#27: Fixed tagging of architecture components.
-
#29: Documentation: removed duplicated generation of DSL command docs.
Deprecations:
-
Command
printNodes
: only pass two arguments; do not pass the list of nodes. So far, the old version is still there but writes a warning message.
6.16. Release 1.1.0 — 04.02.2017
-
#16: Improved Maven configuration.
-
#14: NEW: Gradle Plugin
-
#18: Now the component name "Rest" which is used by convention to collect all left-over classes is no longer hard-coded. NOTE: This requires to filter away that node before calling
checkArchitectureRules
.
Do something like:
checkArchitectureRules(architectureTView.filter(nodeNameOutFilter("Rest")), architecture("T-View"))
-
#21: Minor change to the code mapping (see Architecture Model DSL Reference): uses
AntPathMatcher
implementation. This should not break common architecture definitions. -
#22: Fixed a bug which prevented QAvalidator to see dependencies to type parameters in generic types.
-
#22: Improved cycle detection. Now unrelated cycles are identified separately. Base relations are logged.
6.18. Release 0.9.1 — 12.12.2016
-
Change default for
allClassesGraph
: only filters outjava.lang.*
(used to filterjava.util.*
,java.io.*
, andorg.slf4j.*
)
6.19. Release 0.9 — 12.12.2016
-
Analysis Engine:
-
reads Java
.class
files -
reads Maven
pom.xml
files
-
-
Output:
-
produces GraphViz
.dot
files -
produces
.graphml
files for use with yEd -
writes a log file for SonarQube
-
-
UI:
-
allows to navigate the graph
-
uses Lucene queries to find nodes based on their properties
-
7. QAvalidator Backlog and Roadmap
This chapter lists features and improvements which are comping up but not yet planned exactly, as well as ideas for features which are not yet concrete enough.
7.1. Features
-
Combine static and dynamic analysis:
-
read traces and include them in the analysis.
-
-
create jQAssistant constraints ("concepts") from an architecture, and let jQAssistant do the rest of the work.
-
This could nicely integrate with projects that do have a jQAssistant configuration, and want to add QAvalidator’s architecture description.
-
-
What-If analysis:
-
i.e. virtual refactorings and the analysis of the effects.
-
see also the batch-renaming tool at QAware. That input could be used here.
-
-
More input scopes:
-
Analyse JavaScript source code.
-
Find dependencies between TypeScript code and Java code and/or REST endpoints, or regarding the message types (DTOs).
-
8. Other Tools
There are many other tools which claim to help on static analysis and architecture validation. Some of them may be worth looking at, while QAvalidator may beat others — at least for the use case it’s intended for.
- Structure101
-
Better for interactive analysis. See [s101].
- Sonargraph
-
See [Sonargraph]: They say about themselves: "Sonargraph-Explorer is a simple but powerful static analysis tool with a focus on metrics and dependency visualization."
- SonarQube
-
SonarQube [SonarQube] offers a view called the "component design". It offers a matrix view on packages and their dependencies. It’s a little hard to read and work with; as long as the system is fairly clean (not too tangled), it’s ok to find a cycle. SonarCube does not allow to define something like an intended architecture.
- jQAssistant
-
See [jQAssistant], jQAssistant is the new kid on the block for static code analysis. jQAssistant has a similar approach as QAvalidator: It reads the byte code to find dependencies; it also considers the system to form a graph with classes as the nodes, and dependencies as the edges. jQAssistant offers nice analysis options and an interactive way to explore the classes graph. It knows a concept to define rules which declare unwanted dependencies. It does not allow to model the intended architecture.
- JDepend
-
Project seems to be dead. See [JDepend].
For a discussion of more tools, see [AP11], chapter 6.
Bibliography and Links
-
[AP11] Josef Adersberger, Michael Philippsen, ReflexML: UML-based Architecture-to-code Traceability and Consistency Checking, ECSA 2011
-
[GraphViz] GraphViz, http://www.graphviz.org/ (last checked: 04.02.2016)
-
[JDepend] JDepend, http://clarkware.com/software/JDepend.html (last checked: 30.08.2016)
-
[jQAssistant] jQAssistant, https://jqassistant.org/, (last checked: 29.08.2016)
-
[s101] Structure101, http://structure101.com/, (last checked: 30.08.2016)
-
[Sonargraph] Sonargraph, https://www.hello2morrow.com/products/sonargraph (last checked: 17.02.2016)
-
[SonarQube] SonarQube, http://www.sonarqube.org/, (last checked: 29.08.2016)
-
[yEd] yEd, http://www.yworks.com/, (last checked: 12.12.2016)


 It worked. At least I believe so because the company who tested me never said anything again. However, I wanted to know for sure, so I began asking the Human Resources person for my test results, and surprise, surprise---they stonewalled me and would not give them to me. My OWN test results!! Can you believe that? They acted like I needed to subpoena them if I wanted my own test results, and indeed I probably would have had to. But most relevant to this thread is that I had only stopped smoking for about three days before taking the test, so it appeared to be very effective. At that time, it was a clear, slightly viscous liquid, but nowadays it's a white powder that apparently dissolves just fine.
Experience Reports are the writings and opinions of the individual authors who submit them.
Some of the activities described are dangerous and/or illegal and none are recommended by Erowid.


---
layout: post
title: Lots of connections with lots of other funny business...
date: '2008-12-12T23:16:00.001-06:00'
author: Jabell
tags:
- Politics
- Humor
modified_time: '2008-12-12T23:18:15.652-06:00'
blogger_id: tag:blogger.com,1999:blog-19739956.post-3002246066654004290
blogger_orig_url: http://theabellsix.blogspot.com/2008/12/lots-of-connections-with-lots-of-other.html
---
"I am not saying that Obama had any connection with this particular eBay treatment of his old Senate seat.
Read the whole thing.


I'm currently working as a programmer and system developer. I do create windows, mobile and web apps. I have a passion when it comes to technologies and innovations. I'm also active in sharing my knowledge and skills as a community technical resource speaker in some universities and organizations here in the Philippines.
Because of my passion for sharing and contributing to the community, I was nominated and received an award as one of the Microsoft MVPs (Most Valuable Professionals). I'm still an active Microsoft MVP in the Philippines until now.
This award is given to exceptional technical community leaders who share their remarkable passion, real-world knowledge, and technical expertise with others through demonstration of exemplary commitment. We appreciate your outstanding contributions in the Visual Studio and Development Technologies technical communities during the past year.
I'm one of the
authors in Planet Xamarin (Blog Community). It composed of active xamarin bloggers around the world.
In my free time, I usually play logical games, learning new things and exploring different things. I like to travel and take some photos and videos. Simply, I'm just a normal adventurer who seeks a lot of different adventures. (Oh, Come on! we only live once right? :p) Follow me on my
IG Account
By the way, when it comes to drinks? I don't drink coffee and I already stopped that for almost 2 years but I'm addicted to hot tea. I don't drink alcoholic beverage except for wine. In Addition, I like milk and fruit juice too.
I'm a simple programmer but my dreams are extraordinary :)


The TeX Live package is a comprehensive TeX document production system. It includes TeX, LaTeX2e, ConTeXt, Metafont, MetaPost, BibTeX and many other programs; an extensive collection of macros, fonts and documentation; and support for typesetting in many different scripts from around the world.
It is necessary to use a binary installer for the first install.
This will provide the programs, the scripts, and a lot of
supporting files and documentation. After that, you can rebuild
most of the programs from source by following the instructions for
texlive-20140525. The installer is updated
frequently, so its md5sum will change if it is newer than what is
shown below. Newer versions of the installer are expected to work
with these instructions, for so long as they install to a
2014/
directory.
This package is known to build and work properly using an LFS-7.6 systemd platform.
Download (HTTP): http://mirror.ctan.org/systems/texlive/tlnet/install-tl-unx.tar.gz
Download MD5 sum: f5b1e909d5c2380cefd2cda83d288c00 (at 20140628)
Download size: 3.2 MB
Estimated disk space required: 3.8 GB
Estimated build time: varies, depending on network speed and traffic
The binaries are mostly linked to static libraries such as
libc.a
, but a few of the programs and
several scripts will fail if the following packages are not
present.
ghostscript-9.14 is dynamically loaded by dvisvgm, which is used by asy.
Xorg Libraries and libxcb-1.11 are needed for inimf, mf, pdfclose, pdfopen and xdvixaw. But if you are using asy, or using TeX to create a PDF file, you will need an X Window System (for PDF files, this is to support a PDF viewer of your choice, for example epdfview-0.1.8).
the optional non-wide-character ncurses library (for "some binary-only application") from the bottom of the Ncurses page in LFS is needed for xindy.run which is used by xindy
If you are building on 32-bit x86, the binary version of asy needs
FFTW, GLU-9.0.0 and
libreadline-5.2
: this only requires libreadline.so.5.2
which can be manually copied
from the shm/
directory after running
configure and
make and then
symlinked as libreadline.so.5
.
Python-2.7.8 is used by many scripts.
Ruby-2.1.2 is used by some scripts, mostly within mtx_context which is part of conTeXt.
User Notes: http://wiki.linuxfromscratch.org/blfs/wiki/tl-installer
The TeX Live set of programs with
its supporting documents, fonts, and utilities is very large. The
upstream maintainers recommend placing all files in a single
directory structure. BLFS recommends /opt/texlive
.
First, unpack the installer and change into the installer's
directory, install-tl-<CCYYMMDD>
. This directory name
changes when the installer is updated, so replace <CCYYMMDD>
by the correct directory name.
tar -xf install-tl-unx.tar.gz && cd install-tl-<CCYYMMDD>
The distribution binaries installed below use static linking for general linux system libraries. Additional libraries or interpreters as specified in the dependencies section do not need to be present during the install, but the programs that need them will not run until their specific dependencies have been installed.
Now, as the root
user:
TEXLIVE_INSTALL_PREFIX=/opt/texlive ./install-tl
This command is interactive and allows selection or modification of platform, packages, directories, and other options. The full installation scheme will require about 3.8 gigabytes of disk space. The time to complete the download will depend on your internet connection speed and the number of packages selected.
After the package download is complete, the next step is to make
sure that the system can properly find the files. If you set up
your login scripts as recommended in The
Bash Shell Startup Files, update the needed paths by appending
to the extrapaths.sh
script. The
programs are always installed in an <ARCH>-linux
subdirectory. Always replace
<ARCH> by x86_64, for 64bit, or by i386, for 32bit,
respectively, depending on the architecture that you are using in
your actual installation location.
cat >> /etc/profile.d/extrapaths.sh << "EOF" pathappend /usr/share/man MANPATH pathappend /opt/texlive/2014/texmf-dist/doc/man MANPATH pathappend /usr/share/info INFOPATH pathappend /opt/texlive/2014/texmf-dist/doc/info INFOPATH pathappend /opt/texlive/2014/bin/<ARCH>-linux EOF
The standard MANPATH and INFOPATH path are specified above to ensure they are included. If they are already set in the boot script procedure, the pathappend function will ensure duplicates are removed, so including them here will do no harm.
The new paths can be immediately activated by running source /etc/profile.
At this point the binary installation is complete.
./install-tl --location
http://mirror.aut.ac.nz/CTAN/systems/texlive/tlnet/
: use a
variation of this if you wish to use a different mirror, e.g.
because you are in New Zealand but the installer chooses to use an
Australian mirror. The list of mirrors is at
http://ctan.org/mirrors.
Last updated on 2014-09-15 23:11:39 -0700


Members
onContinue :function
Callback for incremental rendering -- a function that will be called
each time the rendering is paused. To continue rendering call the
function that is the first argument to the callback.
Type:
- function
- Source:
promise :Promise
Promise for rendering task completion.
Type:
- Promise
- Source:
Methods
cancel()
Cancels the rendering task. If the task is currently rendering it will
not be cancelled until graphics pauses with a timeout. The promise that
this object extends will be rejected when cancelled.
- Source:


Andrew is a UCSD 2nd-year student majoring in Computer Science and minoring in Cognitive Science.
Go Fish game
Tingz1
Tingz2
Tingz3
Tingz4
Tingz5
Tingz6
Tingz7


Kerala is divided into 14 districts with the capital being Thiruvananthapuram. Malayalam is the most widely spoken language and is also the official language of the state.


My Favorie Things
Favorite things 1
Favorite things 2
Favorite things 3
Show me the things!
sweaters
kittens


Islands and Bridges
问题描述 :
Suppose there are n islands. The value of a Hamilton path C1C2…Cn is calculated as the sum of three parts. Let Vi be the value for the island Ci. As the first part, we sum over all the Vi values for each island in the path. For the second part, for each edge CiCi+1 in the path, we add the product Vi*Vi+1. And for the third part, whenever three consecutive islands CiCi+1Ci+2 in the path forms a triangle in the map, i.e. there is a bridge between Ci and Ci+2, we add the product Vi*Vi+1*Vi+2.
Most likely but not necessarily, the best triangular Hamilton path you are going to find contains many triangles. It is quite possible that there might be more than one best triangular Hamilton paths; your second task is to find the number of such paths.
Input
The input file starts with a number q (q<=20) on the first line, which is the number of test cases. Each test case starts with a line with two integers n and m, which are the number of islands and the number of bridges in the map, respectively. The next line contains n positive integers, the i-th number being the Vi value of island i. Each value is no more than 100. The following m lines are in the form x y, which indicates there is a (two way) bridge between island x and island y. Islands are numbered from 1 to n. You may assume there will be no more than 13 islands.
输入:
输出:
Note: A path may be written down in the reversed order. We still think it is the same path.
样例输入:
2 3 3 2 2 2 1 2 2 3 3 1 4 6 1 2 3 4 1 2 1 3 1 4 2 3 2 4 3 4
样例输出:
22 3 69 1
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<string> #include<bitset> using namespace std; #define INF 0xFFFFFF int n,m; long long dp[1<<14][14][14]; long long num[1<<14][14][14]; int maps[14][14]; int v[14]; bool jud(int s,int i) { if(i==0) return true; if(s&(1<<(i-1))) return true; return false; } void solve() { int x,y,tmps,tmp; scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%d",&v[i]); memset(maps,0,sizeof(maps)); for(int i=1;i<=m;i++) { scanf("%d%d",&x,&y); maps[x][y]=maps[y][x]=1; } if(n==1) { cout<<v[1]<<" "<<1<<endl; return; } memset(dp,-1,sizeof(dp)); memset(num,0,sizeof(num)); for(int i=1;i<=n;i++) { int j=0; j|=(1<<(i-1)); dp[j][i][0]=v[i]; num[j][i][0]=1; } for(int i=0;i<(1<<n);i++) { for(int j=1;j<=n;j++) { if(!jud(i,j)) continue; tmps=i^(1<<(j-1)); for(int k=1;k<=n;k++) { if(k==j) continue; if(!jud(i,k)) continue; if(!maps[k][j]) continue; for(int q=0;q<=n;q++) { if(q==k || q==j) continue; if(!maps[q][k] && q) continue; if(!jud(tmps,q)) continue; if(dp[tmps][k][q]==-1) continue; tmp=v[j]+v[j]*v[k]+dp[tmps][k][q]; if(maps[q][j]) tmp+=v[q]*v[j]*v[k]; if(tmp==dp[i][j][k]) num[i][j][k]+=num[tmps][k][q]; if(tmp>dp[i][j][k]) { dp[i][j][k]=tmp; num[i][j][k]=num[tmps][k][q]; } } } } } long long ans1=-1,ans2=0; for(int s=(1<<n)-1,i=1;i<=n;i++) { for(int j=1;j<=n;j++) { if(i==j) continue; if(ans1==dp[s][i][j]) ans2+=num[s][i][j]; if(dp[s][i][j]>ans1) { ans1=dp[s][i][j]; ans2=num[s][i][j]; } } } if(n==1) { cout<<ans1<<" "<<1<<endl; return; } if(ans1==-1) printf("0 0\n"); else cout<<ans1<<" "<<ans2/2<<endl; } int main() { int t; scanf("%d",&t); while(t--) solve(); return 0; }
解题报告转自：http://blog.csdn.net/lenleaves/article/details/7981788
<EMAIL_ADDRESS>顺利测试通过了，多谢
楼主你能把scanf( "%d%*" ,&T );中%*的用法详细说明吗
第二个方法挺不错。NewHead代表新的头节点，通过递归找到最后一个节点之后，就把这个节点赋给NewHead，然后一直返回返回，中途这个值是没有变化的，一边返回一边把相应的指针方向颠倒，最后结束时返回新的头节点到主函数。
We do not harbor that I couldn’t remove other individuals’ problems whenever this arrived at.
Excellent Web-site! I required to ask if I might webpages and use a component of the net web website and use a number of factors for just about any faculty process. Please notify me through email regardless of whether that would be excellent. Many thanks
不考虑最后将结果排序的话，快排的时间复杂度是O(N) ，而堆排的是O(N*logK),这样比较看，快排快
不错，现在就缺少新的资料
顺利测试通过了，多谢
为什么暴力求解方法循环到sqrt(n) 就可以了呢
Good task for the group. Hold it up for every yeara??s winner. This is a excellent oppotunity for a lot more enhancement. Indeed, obtaining far better and much better is constantly the crucial. Just like my pal suggests on the truth about ab muscles, he just keeps obtaining much better.
通不过。
Gucci New Fall Arrivals
This is really nice to know. I hope it will be successful in the future. Good job on this and keep up the good work.
1234百位上出现的次数应该是200吧。


Create a calculator. It should contain all ten numbers, 0 - 9, along with buttons to add, subtract, multiply, divide, and equal. There should be a display that updates as you click buttons. Your calculator should behave as much like a real calculator as possible. Calculations should continue until the equal key is clicked.
The Bootstrap Documentation will be essential for successfully completing this project.


The_Promised_Neverland
At Grace Field House, life couldn't be better for the orphans! Though they have no parents, together with the other kids and a kind "Mama" who cares for them, they form one big, happy family. No child is ever overlooked, especially since they are all adopted by the age of 12. Their daily lives involve rigorous tests, but afterwards, they are allowed to play outside.There is only one rule they must obey: do not leave the orphanage. But one day, two top-scoring orphans, Emma and Norman, venture past the gate and unearth the harrowing secret behind their entire existence. Utilizing their quick-wittedness, the children must work together to somehow change their predetermined fate.


BranndIT is a digital agency, designing, developing and implementing high impact experiences for both online and offline audiences.
We really enjoy what we do, connecting brands and building relationships, but here is why we are peculiar: we have years of experience, believe in relationships and have a gaping knowledge of business processes that empower us to develop products which are truly exciting and extensively impactful.
We keep up with times, always adapting to the newest trends in product design, branding and digital.


Results are returned in JSON by default, but you can change the result format to JSONP, XML, or CSV using the format query string parameter. For instance, add format=jsonp&callback=your_function, format=xml, or format=csv to your query string arguments. (You will need either a ? or an & to separate it from the rest of the URL or arguments, of course.)
You can also specify which fields you want using the fields argument. Use double-underscores to span relationships.
Results are paged 100 per call by default. This is true even in CSV format. Set the limit parameter to a high value to get all of the results at once. Use offset to page through results. The maximum limit is 6000.
You can sort results on a field using the sort parameter on any sortable field (as listed below). Prepend the field name with a minus sign to sort in descending order. To sort on multiple fields, separate fields with the pipe (|) character.
You can filter output on any filterable field (which are indicated in the field documentation below) using query string parameters (e.g. filter1=value1&filter2=value2. Endpoints backed by Django Haystack also support the q field which performs a full-text search. To specify a null value, just use “null”. For boolean fields, use “true” and “false” (lowercase). For date and date-time fields, use ISO format (YYYY-MM-DD or YYYY-MM-DDTHH:MM:SS), like you’ll see in the output.
Besides exact comparisons, you can also use several operators as fieldname__operator. The operators are contains, exact, gt, gte, lt, lte, in, startswith, and range. The in and range operators accept multiple values separated by pipe (|) characters.
CORS is enabled for API URLs without credentials passing.