mirror of
https://github.com/openjdk/jdk.git
synced 2025-09-20 02:54:35 +02:00
8028196: Javac allows timestamps inside rt.jar to affect compilation when using -sourcepath
Added -XXuserPathsFirst to allow user classes to take precedence over boot classes Reviewed-by: jjg
This commit is contained in:
parent
15ebe0dcbf
commit
b2d433dcfc
4 changed files with 430 additions and 10 deletions
|
@ -130,6 +130,12 @@ public class ClassReader {
|
||||||
**/
|
**/
|
||||||
public boolean preferSource;
|
public boolean preferSource;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Switch: Search classpath and sourcepath for classes before the
|
||||||
|
* bootclasspath
|
||||||
|
*/
|
||||||
|
public boolean userPathsFirst;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The currently selected profile.
|
* The currently selected profile.
|
||||||
*/
|
*/
|
||||||
|
@ -270,6 +276,7 @@ public class ClassReader {
|
||||||
saveParameterNames = options.isSet("save-parameter-names");
|
saveParameterNames = options.isSet("save-parameter-names");
|
||||||
cacheCompletionFailure = options.isUnset("dev");
|
cacheCompletionFailure = options.isUnset("dev");
|
||||||
preferSource = "source".equals(options.get("-Xprefer"));
|
preferSource = "source".equals(options.get("-Xprefer"));
|
||||||
|
userPathsFirst = options.isSet(XXUSERPATHSFIRST);
|
||||||
|
|
||||||
profile = Profile.instance(context);
|
profile = Profile.instance(context);
|
||||||
|
|
||||||
|
@ -2649,7 +2656,7 @@ public class ClassReader {
|
||||||
if (c.owner == p) // it might be an inner class
|
if (c.owner == p) // it might be an inner class
|
||||||
p.members_field.enter(c);
|
p.members_field.enter(c);
|
||||||
}
|
}
|
||||||
} else if (c.classfile != null && (c.flags_field & seen) == 0) {
|
} else if (!preferCurrent && c.classfile != null && (c.flags_field & seen) == 0) {
|
||||||
// if c.classfile == null, we are currently compiling this class
|
// if c.classfile == null, we are currently compiling this class
|
||||||
// and no further action is necessary.
|
// and no further action is necessary.
|
||||||
// if (c.flags_field & seen) != 0, we have already encountered
|
// if (c.flags_field & seen) != 0, we have already encountered
|
||||||
|
@ -2695,20 +2702,33 @@ public class ClassReader {
|
||||||
|
|
||||||
private boolean verbosePath = true;
|
private boolean verbosePath = true;
|
||||||
|
|
||||||
|
// Set to true when the currently selected file should be kept
|
||||||
|
private boolean preferCurrent;
|
||||||
|
|
||||||
/** Load directory of package into members scope.
|
/** Load directory of package into members scope.
|
||||||
*/
|
*/
|
||||||
private void fillIn(PackageSymbol p) throws IOException {
|
private void fillIn(PackageSymbol p) throws IOException {
|
||||||
if (p.members_field == null) p.members_field = new Scope(p);
|
if (p.members_field == null)
|
||||||
String packageName = p.fullname.toString();
|
p.members_field = new Scope(p);
|
||||||
|
|
||||||
|
preferCurrent = false;
|
||||||
|
if (userPathsFirst) {
|
||||||
|
scanUserPaths(p);
|
||||||
|
preferCurrent = true;
|
||||||
|
scanPlatformPath(p);
|
||||||
|
} else {
|
||||||
|
scanPlatformPath(p);
|
||||||
|
scanUserPaths(p);
|
||||||
|
}
|
||||||
|
verbosePath = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scans class path and source path for files in given package.
|
||||||
|
*/
|
||||||
|
private void scanUserPaths(PackageSymbol p) throws IOException {
|
||||||
Set<JavaFileObject.Kind> kinds = getPackageFileKinds();
|
Set<JavaFileObject.Kind> kinds = getPackageFileKinds();
|
||||||
|
|
||||||
fillIn(p, PLATFORM_CLASS_PATH,
|
|
||||||
fileManager.list(PLATFORM_CLASS_PATH,
|
|
||||||
packageName,
|
|
||||||
EnumSet.of(JavaFileObject.Kind.CLASS),
|
|
||||||
false));
|
|
||||||
|
|
||||||
Set<JavaFileObject.Kind> classKinds = EnumSet.copyOf(kinds);
|
Set<JavaFileObject.Kind> classKinds = EnumSet.copyOf(kinds);
|
||||||
classKinds.remove(JavaFileObject.Kind.SOURCE);
|
classKinds.remove(JavaFileObject.Kind.SOURCE);
|
||||||
boolean wantClassFiles = !classKinds.isEmpty();
|
boolean wantClassFiles = !classKinds.isEmpty();
|
||||||
|
@ -2748,6 +2768,7 @@ public class ClassReader {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String packageName = p.fullname.toString();
|
||||||
if (wantSourceFiles && !haveSourcePath) {
|
if (wantSourceFiles && !haveSourcePath) {
|
||||||
fillIn(p, CLASS_PATH,
|
fillIn(p, CLASS_PATH,
|
||||||
fileManager.list(CLASS_PATH,
|
fileManager.list(CLASS_PATH,
|
||||||
|
@ -2768,7 +2789,17 @@ public class ClassReader {
|
||||||
sourceKinds,
|
sourceKinds,
|
||||||
false));
|
false));
|
||||||
}
|
}
|
||||||
verbosePath = false;
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scans platform class path for files in given package.
|
||||||
|
*/
|
||||||
|
private void scanPlatformPath(PackageSymbol p) throws IOException {
|
||||||
|
fillIn(p, PLATFORM_CLASS_PATH,
|
||||||
|
fileManager.list(PLATFORM_CLASS_PATH,
|
||||||
|
p.fullname.toString(),
|
||||||
|
EnumSet.of(JavaFileObject.Kind.CLASS),
|
||||||
|
false));
|
||||||
}
|
}
|
||||||
// where
|
// where
|
||||||
private void fillIn(PackageSymbol p,
|
private void fillIn(PackageSymbol p,
|
||||||
|
|
|
@ -416,6 +416,8 @@ public enum Option {
|
||||||
|
|
||||||
XPREFER("-Xprefer:", "opt.prefer", EXTENDED, BASIC, ONEOF, "source", "newer"),
|
XPREFER("-Xprefer:", "opt.prefer", EXTENDED, BASIC, ONEOF, "source", "newer"),
|
||||||
|
|
||||||
|
XXUSERPATHSFIRST("-XXuserPathsFirst", "opt.userpathsfirst", HIDDEN, BASIC),
|
||||||
|
|
||||||
// see enum PkgInfo
|
// see enum PkgInfo
|
||||||
XPKGINFO("-Xpkginfo:", "opt.pkginfo", EXTENDED, BASIC, ONEOF, "always", "legacy", "nonempty"),
|
XPKGINFO("-Xpkginfo:", "opt.pkginfo", EXTENDED, BASIC, ONEOF, "always", "legacy", "nonempty"),
|
||||||
|
|
||||||
|
|
|
@ -240,6 +240,8 @@ javac.opt.printRounds=\
|
||||||
Print information about rounds of annotation processing
|
Print information about rounds of annotation processing
|
||||||
javac.opt.printProcessorInfo=\
|
javac.opt.printProcessorInfo=\
|
||||||
Print information about which annotations a processor is asked to process
|
Print information about which annotations a processor is asked to process
|
||||||
|
javac.opt.userpathsfirst=\
|
||||||
|
Search classpath and sourcepath for classes before the bootclasspath instead of after
|
||||||
javac.opt.prefer=\
|
javac.opt.prefer=\
|
||||||
Specify which file to read when both a source file and class file are found for an implicitly compiled class
|
Specify which file to read when both a source file and class file are found for an implicitly compiled class
|
||||||
javac.opt.AT=\
|
javac.opt.AT=\
|
||||||
|
|
385
langtools/test/tools/javac/options/xprefer/XPreferTest.java
Normal file
385
langtools/test/tools/javac/options/xprefer/XPreferTest.java
Normal file
|
@ -0,0 +1,385 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @test
|
||||||
|
* @summary Tests which path is used to represent an implicit type given
|
||||||
|
* various xprefer arguments and multiple .class / .java files involved.
|
||||||
|
* @bug 8028196
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileWriter;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.io.StringWriter;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.ListIterator;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
|
import java.util.Scanner;
|
||||||
|
|
||||||
|
import javax.tools.JavaCompiler;
|
||||||
|
import javax.tools.JavaCompiler.CompilationTask;
|
||||||
|
import javax.tools.ToolProvider;
|
||||||
|
|
||||||
|
|
||||||
|
public class XPreferTest {
|
||||||
|
|
||||||
|
enum Dir {
|
||||||
|
SOURCE_PATH("src"),
|
||||||
|
CLASS_PATH("cp"),
|
||||||
|
BOOT_PATH("boot");
|
||||||
|
|
||||||
|
File file;
|
||||||
|
Dir(String dir) {
|
||||||
|
this.file = new File(dir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ImplicitOption {
|
||||||
|
XPREFER_SOURCE("-Xprefer:source"),
|
||||||
|
XPREFER_NEWER("-Xprefer:newer"),
|
||||||
|
XXUSERPATHSFIRST("-XXuserPathsFirst");
|
||||||
|
|
||||||
|
final String optionString;
|
||||||
|
private ImplicitOption(String optionString) {
|
||||||
|
this.optionString = optionString;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final static JavaCompiler comp = ToolProvider.getSystemJavaCompiler();
|
||||||
|
final static File OUTPUT_DIR = new File("out");
|
||||||
|
|
||||||
|
public static void main(String... args) throws Exception {
|
||||||
|
|
||||||
|
// Initialize test-directories
|
||||||
|
OUTPUT_DIR.mkdir();
|
||||||
|
for (Dir dir : Dir.values())
|
||||||
|
dir.file.mkdir();
|
||||||
|
|
||||||
|
int testCaseCounter = 0;
|
||||||
|
|
||||||
|
for (List<Dir> dirSubset : SubseqIter.subseqsOf(Dir.values())) {
|
||||||
|
|
||||||
|
if (dirSubset.isEmpty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
for (ImplicitOption policy : ImplicitOption.values()) {
|
||||||
|
for (List<Dir> dirOrder : PermutationIterator.permutationsOf(dirSubset)) {
|
||||||
|
new TestCase(dirOrder, policy, testCaseCounter++).run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class TestCase {
|
||||||
|
|
||||||
|
String classId;
|
||||||
|
List<Dir> dirs;
|
||||||
|
ImplicitOption option;
|
||||||
|
|
||||||
|
public TestCase(List<Dir> dirs, ImplicitOption option, int testCaseNum) {
|
||||||
|
this.dirs = dirs;
|
||||||
|
this.option = option;
|
||||||
|
this.classId = "XPreferTestImplicit" + testCaseNum;
|
||||||
|
}
|
||||||
|
|
||||||
|
void run() throws Exception {
|
||||||
|
|
||||||
|
System.out.println("Test:");
|
||||||
|
System.out.println(" Class id: " + classId);
|
||||||
|
System.out.println(" Dirs: " + dirs);
|
||||||
|
System.out.println(" Option: " + option);
|
||||||
|
|
||||||
|
createTestFiles();
|
||||||
|
String compileOutput = compile();
|
||||||
|
Dir actual = getChosenOrigin(compileOutput);
|
||||||
|
Dir expected = getExpectedOrigin();
|
||||||
|
|
||||||
|
System.out.println(" Expected: " + expected);
|
||||||
|
System.out.println(" Actual: " + actual);
|
||||||
|
|
||||||
|
if (actual != expected) {
|
||||||
|
throw new RuntimeException(String.format(
|
||||||
|
"Expected javac to choose %s but %s was chosen",
|
||||||
|
expected == null ? "<none>" : expected.name(),
|
||||||
|
actual == null ? "<none>" : actual.name()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Dir getExpectedOrigin() {
|
||||||
|
|
||||||
|
Dir newest = dirs.get(0);
|
||||||
|
|
||||||
|
switch (option) {
|
||||||
|
|
||||||
|
case XPREFER_NEWER:
|
||||||
|
|
||||||
|
Dir cls = dirs.contains(Dir.BOOT_PATH) ? Dir.BOOT_PATH
|
||||||
|
: dirs.contains(Dir.CLASS_PATH) ? Dir.CLASS_PATH
|
||||||
|
: null;
|
||||||
|
|
||||||
|
Dir src = dirs.contains(Dir.SOURCE_PATH) ? Dir.SOURCE_PATH
|
||||||
|
: null;
|
||||||
|
|
||||||
|
for (Dir dir : dirs)
|
||||||
|
if (dir == cls || dir == src)
|
||||||
|
return dir;
|
||||||
|
|
||||||
|
return null;
|
||||||
|
|
||||||
|
case XPREFER_SOURCE:
|
||||||
|
return dirs.contains(Dir.SOURCE_PATH) ? Dir.SOURCE_PATH
|
||||||
|
: dirs.contains(Dir.BOOT_PATH) ? Dir.BOOT_PATH
|
||||||
|
: dirs.contains(Dir.CLASS_PATH) ? Dir.CLASS_PATH
|
||||||
|
: null;
|
||||||
|
|
||||||
|
case XXUSERPATHSFIRST:
|
||||||
|
|
||||||
|
for (Dir dir : dirs)
|
||||||
|
if (dir == Dir.SOURCE_PATH || dir == Dir.CLASS_PATH)
|
||||||
|
return dir;
|
||||||
|
|
||||||
|
// Neither SOURCE_PATH nor CLASS_PATH among dirs. Safty check:
|
||||||
|
if (newest != Dir.BOOT_PATH)
|
||||||
|
throw new AssertionError("Expected to find BOOT_PATH");
|
||||||
|
|
||||||
|
return Dir.BOOT_PATH;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new RuntimeException("Unhandled policy case.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Dir getChosenOrigin(String compilerOutput) {
|
||||||
|
Scanner s = new Scanner(compilerOutput);
|
||||||
|
while (s.hasNextLine()) {
|
||||||
|
String line = s.nextLine();
|
||||||
|
if (line.matches("\\[loading .*\\]"))
|
||||||
|
for (Dir dir : Dir.values())
|
||||||
|
if (line.contains(dir.file.getName() + "/" + classId))
|
||||||
|
return dir;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String compile() throws IOException {
|
||||||
|
|
||||||
|
// Create a class that references classId
|
||||||
|
File explicit = new File("ExplicitClass.java");
|
||||||
|
FileWriter filewriter = new FileWriter(explicit);
|
||||||
|
filewriter.append("class ExplicitClass { " + classId + " implicit; }");
|
||||||
|
filewriter.close();
|
||||||
|
|
||||||
|
StringWriter sw = new StringWriter();
|
||||||
|
|
||||||
|
com.sun.tools.javac.Main.compile(new String[] {
|
||||||
|
"-verbose",
|
||||||
|
option.optionString,
|
||||||
|
"-sourcepath", Dir.SOURCE_PATH.file.getPath(),
|
||||||
|
"-classpath", Dir.CLASS_PATH.file.getPath(),
|
||||||
|
"-Xbootclasspath/p:" + Dir.BOOT_PATH.file.getPath(),
|
||||||
|
"-d", XPreferTest.OUTPUT_DIR.getPath(),
|
||||||
|
explicit.getPath()
|
||||||
|
}, new PrintWriter(sw));
|
||||||
|
|
||||||
|
return sw.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
void createTestFiles() throws IOException {
|
||||||
|
long t = 1390927988755L; // Tue Jan 28 17:53:08 CET 2014
|
||||||
|
for (Dir dir : dirs) {
|
||||||
|
createFile(dir).setLastModified(t);
|
||||||
|
t -= 10000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
File createFile(Dir dir) throws IOException {
|
||||||
|
File src = new File(dir.file, classId + ".java");
|
||||||
|
try (FileWriter w = new FileWriter(src)) {
|
||||||
|
w.append("public class " + classId + " {}");
|
||||||
|
}
|
||||||
|
// If we're after the ".java" representation, we're done...
|
||||||
|
if(dir == Dir.SOURCE_PATH)
|
||||||
|
return src;
|
||||||
|
// ...otherwise compile into a ".class".
|
||||||
|
CompilationTask task = comp.getTask(null, null, null, null, null,
|
||||||
|
comp.getStandardFileManager(null, null, null).getJavaFileObjects(src));
|
||||||
|
File dest = new File(dir.file, classId + ".class");
|
||||||
|
if(!task.call() || !dest.exists())
|
||||||
|
throw new RuntimeException("Compilation failure.");
|
||||||
|
src.delete();
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterator for iteration over all subsequences of a given list.
|
||||||
|
class SubseqIter<T> implements Iterator<List<T>> {
|
||||||
|
|
||||||
|
List<T> elements;
|
||||||
|
boolean[] states;
|
||||||
|
|
||||||
|
public SubseqIter(Collection<T> c) {
|
||||||
|
states = new boolean[c.size()];
|
||||||
|
elements = new ArrayList<T>(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Iterable<List<T>> subseqsOf(final T[] t) {
|
||||||
|
return new Iterable<List<T>>() {
|
||||||
|
@Override
|
||||||
|
public Iterator<List<T>> iterator() {
|
||||||
|
return new SubseqIter<T>(Arrays.asList(t));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Roll values in 'states' from index i and forward.
|
||||||
|
// Return true if we wrapped back to zero.
|
||||||
|
private boolean roll(int i) {
|
||||||
|
if (i == states.length)
|
||||||
|
return true;
|
||||||
|
if (!roll(i + 1))
|
||||||
|
return false;
|
||||||
|
states[i] = !states[i];
|
||||||
|
return !states[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<T> next() {
|
||||||
|
if (!hasNext())
|
||||||
|
throw new NoSuchElementException();
|
||||||
|
// Include element i if states[i] is true
|
||||||
|
List<T> next = new ArrayList<T>();
|
||||||
|
for (int i = 0; i < states.length; i++)
|
||||||
|
if (states[i])
|
||||||
|
next.add(elements.get(i));
|
||||||
|
if (roll(0))
|
||||||
|
states = null; // hasNext() == false from now on.
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasNext() {
|
||||||
|
return states != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class PermutationIterator<T> implements Iterator<List<T>> {
|
||||||
|
|
||||||
|
DirInt head;
|
||||||
|
boolean hasNext = true;
|
||||||
|
|
||||||
|
public PermutationIterator(List<T> toPermute) {
|
||||||
|
ListIterator<T> iter = toPermute.listIterator();
|
||||||
|
if (iter.hasNext())
|
||||||
|
head = new DirInt(iter.nextIndex(), iter.next());
|
||||||
|
DirInt prev = head;
|
||||||
|
while (iter.hasNext()) {
|
||||||
|
DirInt di = new DirInt(iter.nextIndex(), iter.next());
|
||||||
|
di.left = prev;
|
||||||
|
prev.right = di;
|
||||||
|
prev = di;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Iterable<List<T>> permutationsOf(final List<T> list) {
|
||||||
|
return new Iterable<List<T>>() {
|
||||||
|
public Iterator<List<T>> iterator() {
|
||||||
|
return new PermutationIterator<>(list);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasNext() {
|
||||||
|
return hasNext;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<T> next() {
|
||||||
|
// Prep return value based on current state
|
||||||
|
List<T> result = new ArrayList<>();
|
||||||
|
for (DirInt di = head; di != null; di = di.right)
|
||||||
|
result.add(di.object);
|
||||||
|
|
||||||
|
// Step state forward
|
||||||
|
DirInt maxMob = null;
|
||||||
|
for (DirInt di = head; di != null; di = di.right)
|
||||||
|
if (di.isMobile() && (maxMob == null || di.val > maxMob.val))
|
||||||
|
maxMob = di;
|
||||||
|
|
||||||
|
if (maxMob == null) {
|
||||||
|
hasNext = false;
|
||||||
|
} else {
|
||||||
|
maxMob.swapWithAdjacent();
|
||||||
|
for (DirInt di = head; di != null; di = di.right)
|
||||||
|
if (di.val > maxMob.val)
|
||||||
|
di.facingLeft = !di.facingLeft;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final class DirInt {
|
||||||
|
int val;
|
||||||
|
T object;
|
||||||
|
DirInt left, right;
|
||||||
|
boolean facingLeft = true;
|
||||||
|
|
||||||
|
public DirInt(int val, T object) {
|
||||||
|
this.val = val;
|
||||||
|
this.object = object;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isMobile() {
|
||||||
|
DirInt adjacent = facingLeft ? left : right;
|
||||||
|
return adjacent != null && val > adjacent.val;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void swapWithAdjacent() {
|
||||||
|
DirInt l = facingLeft ? left : this;
|
||||||
|
DirInt r = facingLeft ? this : right;
|
||||||
|
if (head == l) head = r;
|
||||||
|
if (l.left != null) l.left.right = r;
|
||||||
|
if (r.right != null) r.right.left = l;
|
||||||
|
l.right = r.right;
|
||||||
|
r.left = l.left;
|
||||||
|
r.right = l;
|
||||||
|
l.left = r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue