Unter https://spring.io/blog/2021/01/17/what-s-new-in-spring-boot-2-4
Details siehe https://blog.jetbrains.com/idea/2020/12/intellij-idea-2020-3/.
Eine Vorschau über einige Features in dem Video:
Details siehe https://github.com/spring-projects/spring-boot/releases/tag/v2.4.1
Details siehe https://spring.io/blog/2020/09/16/spring-tools-4-8-0-released. Kurz zusammengefasst:
Download der Spring Tools 4: https://spring.io/tools/
Strings mit einem Zeilenumbruch kommen in Programmen immer wieder vor, etwa bei Bildschirmausgaben, eingebettetem HTML, XML, JSON oder SQL. Ein Beispiel:
String joke =
"Lehrer: '76 % aller Schüler in dieser Klasse haben keine Ahnung von Prozentrechnung.'\n" +
"Schüler: 'Herr Lehrer, so viele sind wir doch gar nicht!'";
Im ersten String-Literal steht \n für den Zeilenumbruch. Das Pluszeichen setzt die beiden Strings zusammen. Der Java-Compiler stellt selbständig aus den beiden Konstanten ein größeres String Literal her.
In Java 15 wurden Textblöcke eingeführt (standardisiert im JEP 378: Text Blocks, https://openjdk.java.net/jeps/378). Damit lassen sich einfacher mehrzeilige Strings aufbauen. Drei doppelte Anführungszeichen leiten einen Textblock ein (opening delimiter genannt) und drei doppelte Anführungszeichen schließen einen Textblock wieder ab (closing delimiter genannt). Mithilfe von Textblöcken sieht unser Beispiel von oben so aus:
String joke = """
Lehrer: '76 % aller Schüler in dieser Klasse haben keine Ahnung von Prozentrechnung.'
Schüler: 'Herr Lehrer, so viele sind wir doch gar nicht!'""";
System.out.println( joke );
Ein Textblock wird immer mit drei doppelten Anführungszeichen eingeleitet, und ein Zeilenumbruch muss folgen. Das bedeutet automatisch, dass die Textblöcke immer mindestens zwei Quellcodezeilen umfassen, und niemals nur in eine Zeile geschrieben werden können.
Nach der Einleitung eines Textblocks hängt der Compiler an jede Zeile, die nicht mit drei Anführungszeichen abgeschlossen wird, einen Zeilenumbruch LINE-FEED, kurs LF (Unicode \u000A). In unserem Beispiel haben wir nur einen Zeilenumbruch. Hätten wir die drei Anführungszeichen in die nächste Zeile geschrieben, hätten wir zwei Zeilenumbrüche im Ergebnis:
String joke = """
Lehrer: '76 % aller Schüler in dieser Klasse haben keine Ahnung von Prozentrechnung.'
Schüler: 'Herr Lehrer, so viele sind wir doch gar nicht!'
""";
Es macht also einen Unterschied, ob die drei Anführungszeichen zum Schließen des Textblocks in einer eigenen Zeile stehen (der entstehende String endet mit einem LF) oder am Ende eines Strings (der entstehende String endet nicht mit einem LF). Beim Schreiben würde das sicherlich etwas symmetrischer und hübscher aussehen, wenn der Beginn eines Textblocks und das Ende eines Textblocks jeweils in einer einzelnen Zeile stehen würden, doch das können wir nicht immer machen, denn das ergibt immer am Ende einen Zeilenumbruch, der vielleicht nicht gewünscht ist.
Textblöcke sind eine alternative Schreibweise für Strings in doppelten Anführungszeichen. Im Bytecode ist später nicht mehr zu erkennen, auf welche Art und Weise der String entstanden ist. Textblöcke können genauso wie reguläre Strings an allen Stellen eingesetzt werden, an denen Strings gefordert werden, etwa als Argument:
System.out.println( """
Lehrer: '76 % aller Schüler in dieser Klasse haben keine Ahnung von Prozentrechnung.'
Schüler: 'Herr Lehrer, so viele sind wir doch gar nicht!'""" );
Ist im Quellcode innerhalb vom Textblock ein Zeilenumbruch sinnvoll, damit z. B. die Zeile nicht so lang wird, lässt sich mit einem Backslash der Zeilenumbruch im Ergebnisstring verhindern:
String joke2 = """
Warum haben Giraffen so einen langen Hals? \
Weil der Kopf so weit oben ist.""";
// Warum haben Giraffen so einen langen Hals? Weil der Kopf so weit oben ist.
System.out.println( joke2 );
Da Textblöcke Strings sind, lassen sie sich auch mit dem +-Operator konkatenieren:
String person1 = "Lehrer", person2 = "Schüler";
String joke = person1 +
"""
: '76 % aller Schüler in dieser Klasse haben keine Ahnung von Prozentrechnung.'
""" + person2 + """
: 'Herr Lehrer, so viele sind wir doch gar nicht!'""";
System.out.println( joke );
Es lässt sich ablesen, dass das Einsetzen von Variableninhalten nicht besonders elegant ist — das war allerdings nicht das Ziel der Textblöcke gewesen, so etwas wie String-Interpolation zu schaffen, was etwa JavaScript kann. Etwas zur Hilfe kommt die String-Methode formatted(…), die als Objektmethode hinzugekommen ist:
String person1 = "Lehrer", person2 = "Schüler";
String joke = """
%s: '76 %% aller Schüler in dieser Klasse haben keine Ahnung von Prozentrechnung.'
%s: 'Herr Lehrer, so viele sind wir doch gar nicht!'""".formatted( person1, person2 );
System.out.println( joke );
Die Einrückung der Zeilen in einem Textblock spielt eine elementare Rolle. Die Zeilen eines Textblocks müssen nicht ganz links am Anfang einer Zeile bei der Position 0 beginnen, sondern dürfen rechts eingerückt sein und den üblichen Konventionen in der Einrückung von Java Programmen folgen. Bei der Einrückung sollte beiläufiger Weißraum (engl. incidental white space) aus dem eigentlichen Textblock ausgenommen werden. Hier wendet der Java-Compiler einen kleinen Algorithmus an. Die Regel ist, dass die Zeile die am weitesten links steht (die Zeile mit dem drei abschließenden Anführungszeichen gehört dazu) den beiläufigen Weißraum bestimmt, der abgeschnitten wird. Es ist dabei egal, ob das Zeichen ein Tabulator oder Leerzeichen ist, auch wenn das auf dem Bildschirm anders aussieht! Bestehen bleibt rechts von dieser Stelle der wesentliche Weißraum (engl. essential white space).
Beispiel 1:
String joke2 = """
Warum haben Giraffen so einen langen Hals?
Weil der Kopf so weit oben ist.""";
Ausgabe 1:
Warum haben Giraffen so einen langen Hals?
Weil der Kopf so weit oben ist.
Beispiel 2:
String joke2 = """
Warum haben Giraffen so einen langen Hals?
Weil der Kopf so weit oben ist.""";
Ausgabe 2:
Warum haben Giraffen so einen langen Hals?
Weil der Kopf so weit oben ist.
Beispiel 3:
String joke2 = """
Warum haben Giraffen so einen langen Hals?
Weil der Kopf so weit oben ist.
""";
Ausgabe 3:
Warum haben Giraffen so einen langen Hals?
Weil der Kopf so weit oben ist.
Kommen wir abschließend noch zu ein paar Regeln. Im Quellcode könnte ein Zeilenende durch unterschiedliche Symbole im Text vorkommen: CR (\u000D), CR-LF (\u000D\u000A) oder LF (\u000A). Der Compiler führt eine sogenannte Normalisierung durch, dass am Ende nur ein LF (\u000A) im String steht.
Textblöcke sind keine sogenannten Raw-Strings, die „roh“ sind, sondern können alle Escape-Sequenzen, unter anderem \n, \t, \‘, \“ und \\, enthalten. So lässt sich am Ende immer noch mit \r eine CR-LF-Sequenz erschaffen:
String joke = """
Lehrer: '76 % aller Schüler in dieser Klasse haben keine Ahnung von Prozentrechnung.'\r
Schüler: 'Herr Lehrer, so viele sind wir doch gar nicht!'""";
Das Ergebnis wäre „Lehrer: ’76 % aller Schüler in dieser Klasse haben keine Ahnung von Prozentrechnung.’\u000D\u000ASchüler: ‚Herr Lehrer, so viele sind wir doch gar nicht!'“.
Kommen im String einfache oder doppelte Anführungszeichen vor, können diese ohne Ausmaskierung eingesetzt werden. Ein Sonderfall ist allerdings, wenn das doppelte Anführungszeichen direkt am Ende vor den drei schließenden doppelten Anführungszeichen steht, dann eine Maskierung notwendig:
String joke = """
Lehrer: "76 % aller Schüler in dieser Klasse haben keine Ahnung von Prozentrechnung."
Schüler: "Herr Lehrer, so viele sind wir doch gar nicht!\"""";
Sollen drei Anführungszeichen selbst in der Ausgabe vorkommen, muss eines der drei Anführungszeichen ausmaskiert werden, um nicht fälschlicherweise als Abschluss interpretiert zu werden. Für die Ausgabe
1 "
2 ""
3 """
4 """"
5 """""
6 """"""
müssen wir schreiben:
System.out.println( """
1 "
2 ""
3 ""\"
4 ""\""
5 ""\"""
6 ""\"""\"""" );
Solche Zeichenketten sind nicht mehr besonders gut lesbar …
Eine weitere Regel ist, dass Leerzeichen am Ende abgeschnitten werden. Aus dem Textblock
String s = """
1␣␣␣␣␣␣␣
2␣␣␣
""";
wird der String „1\u0002“ — der “Offene Kasten” ␣ stehen hier für Leerzeichen, die man sonst im Druck nicht sehen würde.
Sollen Leerzeichen am Ende zählen, so führt man die Escape-Sequenz \s ein. Dies wird übersetzt in ein reguläres Leerzeichen \u0020. Für das obere Beispiel bedeutet das:
String s = """
1 \s
2 \s
""";
Leser können mit der folgenden Aufgabe ihr Verständnis prüfen: Was für ein String entsteht?
String joke2 = """
Warum haben Giraffen so einen langen Hals? \
Weil der Kopf so weit oben ist.
""";
Es entsteht „Warum haben Giraffen so einen langen Hals? Weil der Kopf so weit oben ist.\n“.
Siehe auch http://www.tutego.de/java/JDK-15-oracle-java-15-openjdk-15.html. Wenig spannend für Entwickler. IntelliJ kann direkt mit den Neuerungen umgehen, bei Eclipse muss man erst ein Plugin installieren.
Einige einführende Beiträge:
https://www.infoworld.com/article/3534133/jdk-15-the-new-features-in-java-15.html
https://blog.jetbrains.com/idea/2020/09/java-15-and-intellij-idea/
Download wie immer unter https://www.eclipse.org/eclipseide/.
Was (mich) nervt, ist der zeitgleiche Release von neuen Java-Versionen und der Eclipse IDE. Daher kann die Eclipse-Version Java 15 nur über ein Plugin: https://marketplace.eclipse.org/content/java-15-support-eclipse-2020-09-417
Siehe http://jdk.java.net/15/
2020/06/11 | Rampdown Phase One (fork from main line) | |
2020/07/16 | Rampdown Phase Two | |
2020/08/06 | Initial Release Candidate | |
2020/08/20 | Final Release Candidate | |
2020/09/15 | General Availability |
Sind Variablen final, heißt das lediglich, dass es eine einmalige Zuweisung geben darf. Ob die Werte allerdings zur Laufzeit berechnet werden oder nicht, hat erst einmal nichts mit final zu tun. In folgendem Beispiel ist die Variable eine zur Compilezeit bekannte Konstante:
public class Finance {
public static final int TAX = 19;
}
Greift eine andere Klasse auf die Variable TAX zu, ist das im Quellcode nicht als direkter Variablenzugriff Finance.TAX kodiert, sondern der Compiler hat das Literal 19 direkt an jeder Aufrufstelle eingesetzt. Dies ist eine Optimierung des Compilers, die er laut Java-Spezifikation vornehmen kann.
Wir sprechen in diesem Zusammenhang von einer compile-time constant expression wenn gilt:
Das Einsetzen der konstanten Werte ist praktisch, bringt aber ein Probleme mit sich, wenn das finale Attribut sich ändert. Dann muss nämlich auch jede Klasse übersetzt werden, die Bezug auf die Konstante hatte. Werden die abhängigen Klassen nicht neu übersetzt, ist in ihnen immer noch der alte Wert eincompiliert.
Die Lösung ist, die bezugnehmenden Klassen neu zu übersetzen und sich am besten anzugewöhnen, bei einer Änderung einer Konstanten gleich alles neu zu compilieren. Ein anderer Weg transformiert die finale Variable in eine später initialisierte Form:
public class Finance {
public static final int TAX = Integer.valueOf( 19 );
}
Die Initialisierung findet im statischen Initialisierer statt, und die Konstante mit dem Literal 19 ist zunächst einmal verschwunden. Der Compiler wird also beim Zugriff auf Finance.TAX keine Konstante 19 vorfinden und daher das Literal an den Aufrufstellen nicht einbauen können. In der Klassendatei wird der Bezug Finance.TAX vorhanden sein, und eine Änderung der Konstanten erzwingt keine neue Übersetzung der Klassen.
Siehe https://www.eclipse.org/eclipse/news/4.16/jdt.php zu den Neuerungen.
Zu den Details: https://github.com/google/guava/releases.
guava.gwt.emergency_reenable_rpc
system property to true
. (5214a10)guava-gwt
artifact, not people who use only the guava
artifact. This release contains no changes that break binary compatibility for any users.ImmutableList
, visit guava.dev/ImmutableList. Also, more easily access the index at guava.dev/api.collect
: Annotated FluentIterable.from(FluentIterable)
with @DoNotCall
. (b1c77b7)collect
: Made ceiling
, floor
, headSet(E, boolean)
, and tailSet(E, boolean)
methods available in the GWT-emulated ImmutableSortedSet
. (7e0fe90, 5f2fbf2)graph
: Made it possible to set a stable incident edge order by calling the newly added method [Value]Graph.Builder.incidentEdgeOrder(ElementOrder.stable())
. (7016402)graph
: Added incidentEdgeOrder()
to the [Value]Graph
interfaces. (cde576e)util.concurrent
: Added Duration
-based default
methods to ListeningScheduledExecutorService
. (931e83f)util.concurrent
: Added immediateVoidFuture
. (9f3bae5)util.concurrent
: Removed @Beta
from Service
and related classes. (dc46627)util.concurrent
: Deprecated the 1-arg overload of ServiceManager.addListener
. (86e3620)util.concurrent
: Changed the return type of ServiceManager.servicesByState()
to ImmutableSetMultimap
(but also retained a method with the old signature for binary compatibility). (31999ae)util.concurrent
: Made it safe to load the AbstractFuture
class from a ForkJoinPool
thread under a security manager. (6e0c5b5)Die Aufgaben gibt es unter http://tutego.de/javabuch/aufgaben/ — viele Aufgaben sind mit Lösungsvorschlägen. Neue Lösungen nehme ich gerne entgegen und können auch gerne unter den Kommentaren gepostet werden.
http://www.tutego.de/java/Java-14-OpenJDK-14-Oracle-JDK-14-Java-SE-Development-Kit-14.html
Warten wir nun auf Java 15 und darauf, dass es weiter geht und nicht alles nur ein Preview bleibt. Und hoffen wir, dass viel mehr auf Java 11 aufwärts setzen und wir endlich von Java 8 wegkommen.
Java hat seit Version 1.0 eine switch-Anweisung zum Kontrollfluss. Im Wesentlichen basiert die Syntax auf der Programmiersprache C, die auf die 1970er Jahre zurückgeht. In Java 12 wurde eine neue Syntax probeweise eingeführt, in Java 13 verändert und in Java 14 endgültig integriert.
Insgesamt kann switch in vier Formen auftauchen:
Anweisung/Ausdruck | Ab Java-Version | Syntax | Durchfall | vollständige Abdeckung |
Anweisung | 1.0 | : | Ja | Nein |
Anweisung | 14 | -> | Nein | Nein |
Ausdruck | 14 | : | Ja | Ja |
Ausdruck | 14 | -> | Nein | Ja |
Vier Typen von switch
Den ersten Typ haben wir schon ausgiebig betrachtet, schauen wir uns die weiteren Varianten an.
Bei der vereinfachten switch-Anweisung steht hinter dem Label bzw. default kein Doppelpunkt, sondern ein ->. Dieser hat nichts mit Lambda-Ausdrucken zu tun, auch wenn die Symbole gleich sind. Hinter dem Pfeil steht entweder ein Ausdruck, ein Block in geschweiften Klammern oder ein throw-Anweisung, die eine Ausnahme auslöst. Implizit beendet ein break jeden Zweig, es gibt also kein Durchfallen mehr.
Beispiel
String operator = "+"; switch ( operator ) { case "+" -> System.out.println( "Plus" ); case "-" -> { String minus = "Minus"; System.out.println( minus ); } }
Dadurch, dass bei mehreren Anweisungen immer Blöcke gesetzt werden müssen, tritt eine lokale Variable auch nicht aus dem Bereich aus.
Ein default kann gesetzt werden, muss aber nicht. Das switch muss nicht jede Möglichkeit abdecken, was bei Zahlen und Strings eh nicht funktioniert.
Beispiel
String operator = "+"; switch ( operator ) { case "+" -> System.out.println( "Plus" ); case "-" -> System.out.println( "Minus" ); default -> System.out.println( "Unbekannter Operator" ); }
Bei vereinfachten switch-Anweisungen sind mehrere Labels möglich, die die gleiche Behandlung haben. Kommas trennen die Labels.
Beispiel
String operator = "+"; switch ( operator ) { case "+" -> System.out.println( "Plus" ); case "*", "×" -> System.out.println( "Mal" ); }
Traditionell finden sich die Fallunterscheidungen mit switch als Anweisung und Anweisungen geben nichts zurück. In Java 14 ist es möglich, switch als Ausdruck mit Ergebnis zu nutzen.
Beispiel
String operator = "+"; String writtenOperator = (switch ( operator ) { case "+" -> "Plus"; case "-" -> "Minus"; default -> "Unbekannter Operator"; } ).toUpperCase(); System.out.println( writtenOperator );
Ausdrücke müssen immer Ergebnisse liefern, und folglich muss switch immer einen Pfad auf einen Wert nehmen. Der übliche Fall ist default wie gezeigt, es gibt allerdings Sonderfälle, wie bei Aufzählungen, wo der Compiler prüfen kann, dass alle Möglichkeiten abgedeckt sind.
Beispiel
DayOfWeek today = LocalDate.now().getDayOfWeek(); System.out.println( switch ( today ) { case MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY -> "Arbeit"; case SATURDAY, SUNDAY -> "Party"; } );
Falls rechts neben dem Pfeil kein einfacher Ausdruck steht, sondern ein Block muss auch dieser Block ein Ergebnis zurückgeben. Dafür wird das neue Schlüsselwort yield eingesetzt, hinter dem ein Ausdruck kommt.
Beispiel
String operator = "+"; System.out.println( switch ( operator ) { case "+" -> "Plus"; case "-" -> { String minus = "Minus"; yield minus; } default -> throw new IllegalArgumentException( "Unknown operator" ); } );
Ein Block muss ein yield besitzen oder eine ungeprüfte Ausnahme auslösen.
Auch die Doppelpunkt-Syntax lässt sich als Ausdruck einsetzten, mit ihr ist auch ein Durchfall wieder möglich; ein yield ist zwingend, oder eine ungeprüfte Ausnahme. Die Syntax birgt mit dem Durchfallen eine Fehlerquelle, sodass es vielleicht die schlechteste Variante ist.
Beispiel
String operator = "+"; System.out.println( switch ( operator ) { case "+" : yield "Plus"; case "*" : System.out.println( "Sternchen" ); case "×" : yield "Mal"; default : throw new IllegalArgumentException( "Unknown operator" ); } );
Groovy ist eine Programmiersprache auf der JVM, die bald schon 20 Jahre in der Entwicklung ist. In den letzten Jahren ist es leise um die Sprache geworden und man bekommt den Eindruck, dass Groovy zur Konfigurationssprache für Gradle-Builds degradiert ist.
Nun ist Groovy 3 erschienen mit einem ganz neuen Parser, Parrot. Die Anzahl der Neuerungen ist vielfältig, weil man Groovy nun als echte Erweiterung von Java aufgebaut hat. An vielen kleinen Stellen gab es vorher Fehler, Lambda-Ausdrücke waren anders, Array-Initialisierungen waren mit {} nicht möglich, und vieles mehr.
Zum Nachlesen aller Neuerungen: https://groovy-lang.org/releasenotes/groovy-3.0.html
Gute objektorientiert entworfene Systeme zeichnen sich dadurch aus, dass es eine hohe Interaktion mit anderen Objekten gibt. Idealerweise zerlegt eine Klasse ein Problem nur bis zu dem Punkt, an dem es sich einer anderen Klasse bedienen kann, die dieses einfachere Problem löst. Schwierig wird es, wenn eine eigene Klasse auf eine andere komplexe Klasse zurückgreift und das Objekt nur dann sinnvoll arbeitet, wenn das referenzierte Objekt da ist und irgendwie sinnvoll antwortet. Diese Abhängigkeit ist ungünstig, denn das Ziel eines guten Tests besteht ja darin, lokal zu sein, also die eigentliche Klasse zu testen und nicht alle referenzierten Klassen um sie herum gleich mit.
In der Praxis begegnen uns drei Hilfskonstrukte, die die Lokalität von Tests ermöglichen:
Diese drei Typen können wir unter dem Oberbegriff Dummy-Objekt zusammenfassen. Grundsätzlich gilt bei den vier Begriffen aber, dass sie von Autoren nicht einheitlich verwendet werden.[1]
Beispiel: Mockito-Beispiel
Nehmen wir an, alles aus org.mockito.Mockito.* ist statisch importiert und wir wollen eine java.util.List aufbauen. Dazu muss Mockito erst etwas aufbauen, was sich wie List verhält:
List<?> mockedList = mock( List.class );
Im nächsten Schritt muss das Verhalten der speziellen Liste bestimmt werden:
when( mockedList.get(0) ).thenReturn( „tutego“ );
Anschließend ist die Liste bereit zur Nutzung:
System.out.println( mockedList.get(0) ); // tutego
[1] Die Seite http://xunitpatterns.com/Mocks,%20Fakes,%20Stubs%20and%20Dummies.html stellt einige Autoren mit ihrer Begriffsnutzung vor.
Main program:
package com.tutego.jaxb; import javax.xml.bind.JAXBContext; import javax.xml.bind.Marshaller; import java.io.PrintWriter; public class App { public static void main( String[] args ) throws Exception { Dog dog = new Dog(); dog.name = "Wüffi"; Flea flea = new Flea(); flea.name = "<><> Böser Floh <><>"; dog.flea = flea; JAXBContext jaxbContext = JAXBContext.newInstance( dog.getClass() ); Marshaller jaxbMarshaller = jaxbContext.createMarshaller(); jaxbMarshaller.setProperty( Marshaller.JAXB_FORMATTED_OUTPUT, true ); jaxbMarshaller.marshal( dog, new CDATAContentHandler( new PrintWriter( System.out ) ) ); } }
Dog and Flea:
package com.tutego.jaxb; import javax.xml.bind.annotation.XmlRootElement; @XmlRootElement public class Dog { public String name; public Flea flea; } class Flea { public String name; }
CDATAContentHandler:
package com.tutego.jaxb; import com.sun.xml.txw2.output.CharacterEscapeHandler; import com.sun.xml.txw2.output.DataWriter; import org.xml.sax.SAXException; import java.io.IOException; import java.io.Writer; public class CDATAContentHandler extends DataWriter { public CDATAContentHandler( Writer writer ) { super( writer, "UTF-8", MinimumEscapeHandler.theInstance ); } @Override public void characters( char[] ch, int start, int length ) throws SAXException { boolean useCData = false; loop: for ( int i = start; i < start + length; i++ ) switch ( ch[ i ] ) { case '<': case '>': case '&': useCData = true; break loop; } if ( useCData ) super.startCDATA(); super.characters( ch, start, length ); if ( useCData ) super.endCDATA(); } } /** * Performs no character escaping. Usable only when the output encoding * is UTF, but this handler gives the maximum performance. * * @author Kohsuke Kawaguchi (kohsuke.kawaguchi@sun.com) */ class MinimumEscapeHandler implements CharacterEscapeHandler { private MinimumEscapeHandler() { } // no instanciation please public static final CharacterEscapeHandler theInstance = new MinimumEscapeHandler(); public void escape( char[] ch, int start, int length, boolean isAttVal, Writer out ) throws IOException { // avoid calling the Writerwrite method too much by assuming // that the escaping occurs rarely. // profiling revealed that this is faster than the naive code. int limit = start + length; for ( int i = start; i < limit; i++ ) { char c = ch[ i ]; if ( c == '&' || c == '<' || c == '>' || c == '\r' || (c == '\n' && isAttVal) || (c == '\"' && isAttVal) ) { if ( i != start ) out.write( ch, start, i - start ); start = i + 1; switch ( ch[ i ] ) { case '&': out.write( "&" ); break; case '<': out.write( "<" ); break; case '>': out.write( ">" ); break; case '\"': out.write( """ ); break; case '\n': case '\r': out.write( "&#" ); out.write( Integer.toString( c ) ); out.write( ';' ); break; default: throw new IllegalArgumentException( "Cannot escape: '" + c + "'" ); } } } if ( start != limit ) out.write( ch, start, limit - start ); } }
Zu den Details https://docs.gradle.org/6.0/release-notes.html:
Zu den Details siehe https://developers.redhat.com/blog/2019/11/11/new-features-in-red-hat-codeready-studio-12-13-0-ga-and-jboss-tools-4-13-0-final-for-eclipse-2019-09/
Bei der Update-Liste ist allerdings das meiste von Eclipse …