/*
* Copyright 2017 TNG Technology Consulting GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.tngtech.archunit.library;
import com.tngtech.archunit.PublicAPI;
import com.tngtech.archunit.core.domain.AccessTarget.FieldAccessTarget;
import com.tngtech.archunit.core.domain.JavaAccess.Functions.Get;
import com.tngtech.archunit.core.domain.JavaClass;
import com.tngtech.archunit.core.domain.JavaFieldAccess;
import com.tngtech.archunit.lang.ArchCondition;
import com.tngtech.archunit.lang.ArchRule;
import static com.tngtech.archunit.PublicAPI.Usage.ACCESS;
import static com.tngtech.archunit.base.DescribedPredicate.not;
import static com.tngtech.archunit.core.domain.AccessTarget.Predicates.constructor;
import static com.tngtech.archunit.core.domain.AccessTarget.Predicates.declaredIn;
import static com.tngtech.archunit.core.domain.JavaAccess.Predicates.originOwner;
import static com.tngtech.archunit.core.domain.JavaCall.Predicates.target;
import static com.tngtech.archunit.core.domain.JavaClass.Predicates.assignableTo;
import static com.tngtech.archunit.core.domain.JavaClass.Predicates.resideInAPackage;
import static com.tngtech.archunit.core.domain.properties.HasName.Predicates.name;
import static com.tngtech.archunit.core.domain.properties.HasOwner.Predicates.With.owner;
import static com.tngtech.archunit.core.domain.properties.HasParameterTypes.Predicates.parameterTypes;
import static com.tngtech.archunit.core.domain.properties.HasType.Functions.GET_TYPE;
import static com.tngtech.archunit.lang.conditions.ArchConditions.accessField;
import static com.tngtech.archunit.lang.conditions.ArchConditions.callCodeUnitWhere;
import static com.tngtech.archunit.lang.conditions.ArchConditions.callMethodWhere;
import static com.tngtech.archunit.lang.conditions.ArchConditions.setFieldWhere;
import static com.tngtech.archunit.lang.conditions.ArchPredicates.is;
import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses;
public final class GeneralCodingRules {
private GeneralCodingRules() {
}
@PublicAPI(usage = ACCESS)
public static final ArchCondition<JavaClass> ACCESS_STANDARD_STREAMS = accessStandardStreams();
private static ArchCondition<JavaClass> accessStandardStreams() {
ArchCondition<JavaClass> accessToSystemOut = accessField(System.class, "out");
ArchCondition<JavaClass> accessToSystemErr = accessField(System.class, "err");
ArchCondition<JavaClass> callOfPrintStackTrace = callMethodWhere(
target(name("printStackTrace"))
.and(target(owner(assignableTo(Throwable.class))))
.and(target(parameterTypes(new Class[0]))));
return accessToSystemOut.or(accessToSystemErr).or(callOfPrintStackTrace).as("access standard streams");
}
/**
* It is generally good practice to use correct logging instead of writing to the console.
* <ul>
* <li>Writing to the console cannot be configured in production</li>
* <li>Writing to the console is synchronized and can lead to bottle necks</li>
* </ul>
*/
@PublicAPI(usage = ACCESS)
public static final ArchRule NO_CLASSES_SHOULD_ACCESS_STANDARD_STREAMS =
noClasses().should(ACCESS_STANDARD_STREAMS);
@PublicAPI(usage = ACCESS)
public static final ArchCondition<JavaClass> THROW_GENERIC_EXCEPTIONS = throwGenericExceptions();
private static ArchCondition<JavaClass> throwGenericExceptions() {
ArchCondition<JavaClass> creationOfThrowable =
callCodeUnitWhere(target(is(constructor()).and(is(declaredIn(Throwable.class)))));
ArchCondition<JavaClass> creationOfException =
callCodeUnitWhere(target(is(constructor()).and(is(declaredIn(Exception.class))))
.and(not(originOwner(is(assignableTo(Exception.class))))));
ArchCondition<JavaClass> creationOfRuntimeException =
callCodeUnitWhere(target(is(constructor()).and(is(declaredIn(RuntimeException.class))))
.and(not(originOwner(is(assignableTo(RuntimeException.class))))));
return creationOfThrowable.or(creationOfException).or(creationOfRuntimeException).as("throw generic exceptions");
}
/**
* It is generally good practice to throw specific exceptions like {@link java.lang.IllegalArgumentException}
* or custom exceptions, instead of throwing generic exceptions like {@link java.lang.RuntimeException}.
*/
@PublicAPI(usage = ACCESS)
public static final ArchRule NO_CLASSES_SHOULD_THROW_GENERIC_EXCEPTIONS =
noClasses().should(THROW_GENERIC_EXCEPTIONS);
@PublicAPI(usage = ACCESS)
public static final ArchCondition<JavaClass> USE_JAVA_UTIL_LOGGING =
setFieldWhere(resideInAPackage("java.util.logging..")
.onResultOf(Get.<JavaFieldAccess, FieldAccessTarget>target().then(GET_TYPE)))
.as("use java.util.logging");
/**
* Most projects use the more powerful LOG4J or Logback instead of java.util.logging, often hidden behind
* SLF4J. In this case it's important to ensure consistent use of the agreed logging framework.
*/
@PublicAPI(usage = ACCESS)
public static final ArchRule NO_CLASSES_SHOULD_USE_JAVA_UTIL_LOGGING =
noClasses().should(USE_JAVA_UTIL_LOGGING);
}