src/share/jaxws_classes/com/sun/codemodel/internal/JFormatter.java

Thu, 12 Oct 2017 19:44:07 +0800

author
aoqi
date
Thu, 12 Oct 2017 19:44:07 +0800
changeset 760
e530533619ec
parent 0
373ffda63c9a
permissions
-rw-r--r--

merge

aoqi@0 1 /*
aoqi@0 2 * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
aoqi@0 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
aoqi@0 4 *
aoqi@0 5 * This code is free software; you can redistribute it and/or modify it
aoqi@0 6 * under the terms of the GNU General Public License version 2 only, as
aoqi@0 7 * published by the Free Software Foundation. Oracle designates this
aoqi@0 8 * particular file as subject to the "Classpath" exception as provided
aoqi@0 9 * by Oracle in the LICENSE file that accompanied this code.
aoqi@0 10 *
aoqi@0 11 * This code is distributed in the hope that it will be useful, but WITHOUT
aoqi@0 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
aoqi@0 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
aoqi@0 14 * version 2 for more details (a copy is included in the LICENSE file that
aoqi@0 15 * accompanied this code).
aoqi@0 16 *
aoqi@0 17 * You should have received a copy of the GNU General Public License version
aoqi@0 18 * 2 along with this work; if not, write to the Free Software Foundation,
aoqi@0 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
aoqi@0 20 *
aoqi@0 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
aoqi@0 22 * or visit www.oracle.com if you need additional information or have any
aoqi@0 23 * questions.
aoqi@0 24 */
aoqi@0 25
aoqi@0 26 package com.sun.codemodel.internal;
aoqi@0 27
aoqi@0 28 import java.io.PrintWriter;
aoqi@0 29 import java.io.Writer;
aoqi@0 30 import java.util.ArrayList;
aoqi@0 31 import java.util.Arrays;
aoqi@0 32 import java.util.Collection;
aoqi@0 33 import java.util.HashMap;
aoqi@0 34 import java.util.HashSet;
aoqi@0 35 import java.util.Iterator;
aoqi@0 36 import java.util.List;
aoqi@0 37
aoqi@0 38
aoqi@0 39 /**
aoqi@0 40 * This is a utility class for managing indentation and other basic
aoqi@0 41 * formatting for PrintWriter.
aoqi@0 42 */
aoqi@0 43 public final class JFormatter {
aoqi@0 44 /** all classes and ids encountered during the collection mode **/
aoqi@0 45 /** map from short type name to ReferenceList (list of JClass and ids sharing that name) **/
aoqi@0 46 private HashMap<String,ReferenceList> collectedReferences;
aoqi@0 47
aoqi@0 48 /** set of imported types (including package java types, eventhough we won't generate imports for them) */
aoqi@0 49 private HashSet<JClass> importedClasses;
aoqi@0 50
aoqi@0 51 private static enum Mode {
aoqi@0 52 /**
aoqi@0 53 * Collect all the type names and identifiers.
aoqi@0 54 * In this mode we don't actually generate anything.
aoqi@0 55 */
aoqi@0 56 COLLECTING,
aoqi@0 57 /**
aoqi@0 58 * Print the actual source code.
aoqi@0 59 */
aoqi@0 60 PRINTING
aoqi@0 61 }
aoqi@0 62
aoqi@0 63 /**
aoqi@0 64 * The current running mode.
aoqi@0 65 * Set to PRINTING so that a casual client can use a formatter just like before.
aoqi@0 66 */
aoqi@0 67 private Mode mode = Mode.PRINTING;
aoqi@0 68
aoqi@0 69 /**
aoqi@0 70 * Current number of indentation strings to print
aoqi@0 71 */
aoqi@0 72 private int indentLevel;
aoqi@0 73
aoqi@0 74 /**
aoqi@0 75 * String to be used for each indentation.
aoqi@0 76 * Defaults to four spaces.
aoqi@0 77 */
aoqi@0 78 private final String indentSpace;
aoqi@0 79
aoqi@0 80 /**
aoqi@0 81 * Stream associated with this JFormatter
aoqi@0 82 */
aoqi@0 83 private final PrintWriter pw;
aoqi@0 84
aoqi@0 85 /**
aoqi@0 86 * Creates a JFormatter.
aoqi@0 87 *
aoqi@0 88 * @param s
aoqi@0 89 * PrintWriter to JFormatter to use.
aoqi@0 90 *
aoqi@0 91 * @param space
aoqi@0 92 * Incremental indentation string, similar to tab value.
aoqi@0 93 */
aoqi@0 94 public JFormatter(PrintWriter s, String space) {
aoqi@0 95 pw = s;
aoqi@0 96 indentSpace = space;
aoqi@0 97 collectedReferences = new HashMap<String,ReferenceList>();
aoqi@0 98 //ids = new HashSet<String>();
aoqi@0 99 importedClasses = new HashSet<JClass>();
aoqi@0 100 }
aoqi@0 101
aoqi@0 102 /**
aoqi@0 103 * Creates a formatter with default incremental indentations of
aoqi@0 104 * four spaces.
aoqi@0 105 */
aoqi@0 106 public JFormatter(PrintWriter s) {
aoqi@0 107 this(s, " ");
aoqi@0 108 }
aoqi@0 109
aoqi@0 110 /**
aoqi@0 111 * Creates a formatter with default incremental indentations of
aoqi@0 112 * four spaces.
aoqi@0 113 */
aoqi@0 114 public JFormatter(Writer w) {
aoqi@0 115 this(new PrintWriter(w));
aoqi@0 116 }
aoqi@0 117
aoqi@0 118 /**
aoqi@0 119 * Closes this formatter.
aoqi@0 120 */
aoqi@0 121 public void close() {
aoqi@0 122 pw.close();
aoqi@0 123 }
aoqi@0 124
aoqi@0 125 /**
aoqi@0 126 * Returns true if we are in the printing mode,
aoqi@0 127 * where we actually produce text.
aoqi@0 128 *
aoqi@0 129 * The other mode is the "collecting mode'
aoqi@0 130 */
aoqi@0 131 public boolean isPrinting() {
aoqi@0 132 return mode == Mode.PRINTING;
aoqi@0 133 }
aoqi@0 134
aoqi@0 135 /**
aoqi@0 136 * Decrement the indentation level.
aoqi@0 137 */
aoqi@0 138 public JFormatter o() {
aoqi@0 139 indentLevel--;
aoqi@0 140 return this;
aoqi@0 141 }
aoqi@0 142
aoqi@0 143 /**
aoqi@0 144 * Increment the indentation level.
aoqi@0 145 */
aoqi@0 146 public JFormatter i() {
aoqi@0 147 indentLevel++;
aoqi@0 148 return this;
aoqi@0 149 }
aoqi@0 150
aoqi@0 151 private boolean needSpace(char c1, char c2) {
aoqi@0 152 if ((c1 == ']') && (c2 == '{')) return true;
aoqi@0 153 if (c1 == ';') return true;
aoqi@0 154 if (c1 == CLOSE_TYPE_ARGS) {
aoqi@0 155 // e.g., "public Foo<Bar> test;"
aoqi@0 156 if(c2=='(') // but not "new Foo<Bar>()"
aoqi@0 157 return false;
aoqi@0 158 return true;
aoqi@0 159 }
aoqi@0 160 if ((c1 == ')') && (c2 == '{')) return true;
aoqi@0 161 if ((c1 == ',') || (c1 == '=')) return true;
aoqi@0 162 if (c2 == '=') return true;
aoqi@0 163 if (Character.isDigit(c1)) {
aoqi@0 164 if ((c2 == '(') || (c2 == ')') || (c2 == ';') || (c2 == ','))
aoqi@0 165 return false;
aoqi@0 166 return true;
aoqi@0 167 }
aoqi@0 168 if (Character.isJavaIdentifierPart(c1)) {
aoqi@0 169 switch (c2) {
aoqi@0 170 case '{':
aoqi@0 171 case '}':
aoqi@0 172 case '+':
aoqi@0 173 case '>':
aoqi@0 174 case '@':
aoqi@0 175 return true;
aoqi@0 176 default:
aoqi@0 177 return Character.isJavaIdentifierStart(c2);
aoqi@0 178 }
aoqi@0 179 }
aoqi@0 180 if (Character.isJavaIdentifierStart(c2)) {
aoqi@0 181 switch (c1) {
aoqi@0 182 case ']':
aoqi@0 183 case ')':
aoqi@0 184 case '}':
aoqi@0 185 case '+':
aoqi@0 186 return true;
aoqi@0 187 default:
aoqi@0 188 return false;
aoqi@0 189 }
aoqi@0 190 }
aoqi@0 191 if (Character.isDigit(c2)) {
aoqi@0 192 if (c1 == '(') return false;
aoqi@0 193 return true;
aoqi@0 194 }
aoqi@0 195 return false;
aoqi@0 196 }
aoqi@0 197
aoqi@0 198 private char lastChar = 0;
aoqi@0 199 private boolean atBeginningOfLine = true;
aoqi@0 200
aoqi@0 201 private void spaceIfNeeded(char c) {
aoqi@0 202 if (atBeginningOfLine) {
aoqi@0 203 for (int i = 0; i < indentLevel; i++)
aoqi@0 204 pw.print(indentSpace);
aoqi@0 205 atBeginningOfLine = false;
aoqi@0 206 } else if ((lastChar != 0) && needSpace(lastChar, c))
aoqi@0 207 pw.print(' ');
aoqi@0 208 }
aoqi@0 209
aoqi@0 210 /**
aoqi@0 211 * Print a char into the stream
aoqi@0 212 *
aoqi@0 213 * @param c the char
aoqi@0 214 */
aoqi@0 215 public JFormatter p(char c) {
aoqi@0 216 if(mode==Mode.PRINTING) {
aoqi@0 217 if(c==CLOSE_TYPE_ARGS) {
aoqi@0 218 pw.print('>');
aoqi@0 219 } else {
aoqi@0 220 spaceIfNeeded(c);
aoqi@0 221 pw.print(c);
aoqi@0 222 }
aoqi@0 223 lastChar = c;
aoqi@0 224 }
aoqi@0 225 return this;
aoqi@0 226 }
aoqi@0 227
aoqi@0 228 /**
aoqi@0 229 * Print a String into the stream
aoqi@0 230 *
aoqi@0 231 * @param s the String
aoqi@0 232 */
aoqi@0 233 public JFormatter p(String s) {
aoqi@0 234 if(mode==Mode.PRINTING) {
aoqi@0 235 spaceIfNeeded(s.charAt(0));
aoqi@0 236 pw.print(s);
aoqi@0 237 lastChar = s.charAt(s.length() - 1);
aoqi@0 238 }
aoqi@0 239 return this;
aoqi@0 240 }
aoqi@0 241
aoqi@0 242 public JFormatter t(JType type) {
aoqi@0 243 if(type.isReference()) {
aoqi@0 244 return t((JClass)type);
aoqi@0 245 } else {
aoqi@0 246 return g(type);
aoqi@0 247 }
aoqi@0 248 }
aoqi@0 249
aoqi@0 250 /**
aoqi@0 251 * Print a type name.
aoqi@0 252 *
aoqi@0 253 * <p>
aoqi@0 254 * In the collecting mode we use this information to
aoqi@0 255 * decide what types to import and what not to.
aoqi@0 256 */
aoqi@0 257 public JFormatter t(JClass type) {
aoqi@0 258 switch(mode) {
aoqi@0 259 case PRINTING:
aoqi@0 260 // many of the JTypes in this list are either primitive or belong to package java
aoqi@0 261 // so we don't need a FQCN
aoqi@0 262 if(importedClasses.contains(type)) {
aoqi@0 263 p(type.name()); // FQCN imported or not necessary, so generate short name
aoqi@0 264 } else {
aoqi@0 265 if(type.outer()!=null)
aoqi@0 266 t(type.outer()).p('.').p(type.name());
aoqi@0 267 else
aoqi@0 268 p(type.fullName()); // collision was detected, so generate FQCN
aoqi@0 269 }
aoqi@0 270 break;
aoqi@0 271 case COLLECTING:
aoqi@0 272 final String shortName = type.name();
aoqi@0 273 if(collectedReferences.containsKey(shortName)) {
aoqi@0 274 collectedReferences.get(shortName).add(type);
aoqi@0 275 } else {
aoqi@0 276 ReferenceList tl = new ReferenceList();
aoqi@0 277 tl.add(type);
aoqi@0 278 collectedReferences.put(shortName, tl);
aoqi@0 279 }
aoqi@0 280 break;
aoqi@0 281 }
aoqi@0 282 return this;
aoqi@0 283 }
aoqi@0 284
aoqi@0 285 /**
aoqi@0 286 * Print an identifier
aoqi@0 287 */
aoqi@0 288 public JFormatter id(String id) {
aoqi@0 289 switch(mode) {
aoqi@0 290 case PRINTING:
aoqi@0 291 p(id);
aoqi@0 292 break;
aoqi@0 293 case COLLECTING:
aoqi@0 294 // see if there is a type name that collides with this id
aoqi@0 295 if(collectedReferences.containsKey(id)) {
aoqi@0 296 if( !collectedReferences.get(id).getClasses().isEmpty() ) {
aoqi@0 297 for( JClass type : collectedReferences.get(id).getClasses() ) {
aoqi@0 298 if (type.outer()!=null) {
aoqi@0 299 collectedReferences.get(id).setId(false);
aoqi@0 300 return this;
aoqi@0 301 }
aoqi@0 302 }
aoqi@0 303 }
aoqi@0 304 collectedReferences.get(id).setId(true);
aoqi@0 305 } else {
aoqi@0 306 // not a type, but we need to create a place holder to
aoqi@0 307 // see if there might be a collision with a type
aoqi@0 308 ReferenceList tl = new ReferenceList();
aoqi@0 309 tl.setId(true);
aoqi@0 310 collectedReferences.put(id, tl);
aoqi@0 311 }
aoqi@0 312 break;
aoqi@0 313 }
aoqi@0 314 return this;
aoqi@0 315 }
aoqi@0 316
aoqi@0 317 /**
aoqi@0 318 * Print a new line into the stream
aoqi@0 319 */
aoqi@0 320 public JFormatter nl() {
aoqi@0 321 if(mode==Mode.PRINTING) {
aoqi@0 322 pw.println();
aoqi@0 323 lastChar = 0;
aoqi@0 324 atBeginningOfLine = true;
aoqi@0 325 }
aoqi@0 326 return this;
aoqi@0 327 }
aoqi@0 328
aoqi@0 329 /**
aoqi@0 330 * Cause the JGenerable object to generate source for iteself
aoqi@0 331 *
aoqi@0 332 * @param g the JGenerable object
aoqi@0 333 */
aoqi@0 334 public JFormatter g(JGenerable g) {
aoqi@0 335 g.generate(this);
aoqi@0 336 return this;
aoqi@0 337 }
aoqi@0 338
aoqi@0 339 /**
aoqi@0 340 * Produces {@link JGenerable}s separated by ','
aoqi@0 341 */
aoqi@0 342 public JFormatter g(Collection<? extends JGenerable> list) {
aoqi@0 343 boolean first = true;
aoqi@0 344 if(!list.isEmpty()) {
aoqi@0 345 for (JGenerable item : list) {
aoqi@0 346 if (!first)
aoqi@0 347 p(',');
aoqi@0 348 g(item);
aoqi@0 349 first = false;
aoqi@0 350 }
aoqi@0 351 }
aoqi@0 352 return this;
aoqi@0 353 }
aoqi@0 354
aoqi@0 355 /**
aoqi@0 356 * Cause the JDeclaration to generate source for itself
aoqi@0 357 *
aoqi@0 358 * @param d the JDeclaration object
aoqi@0 359 */
aoqi@0 360 public JFormatter d(JDeclaration d) {
aoqi@0 361 d.declare(this);
aoqi@0 362 return this;
aoqi@0 363 }
aoqi@0 364
aoqi@0 365 /**
aoqi@0 366 * Cause the JStatement to generate source for itself
aoqi@0 367 *
aoqi@0 368 * @param s the JStatement object
aoqi@0 369 */
aoqi@0 370 public JFormatter s(JStatement s) {
aoqi@0 371 s.state(this);
aoqi@0 372 return this;
aoqi@0 373 }
aoqi@0 374
aoqi@0 375 /**
aoqi@0 376 * Cause the JVar to generate source for itself
aoqi@0 377 *
aoqi@0 378 * @param v the JVar object
aoqi@0 379 */
aoqi@0 380 public JFormatter b(JVar v) {
aoqi@0 381 v.bind(this);
aoqi@0 382 return this;
aoqi@0 383 }
aoqi@0 384
aoqi@0 385 /**
aoqi@0 386 * Generates the whole source code out of the specified class.
aoqi@0 387 */
aoqi@0 388 void write(JDefinedClass c) {
aoqi@0 389 // first collect all the types and identifiers
aoqi@0 390 mode = Mode.COLLECTING;
aoqi@0 391 d(c);
aoqi@0 392
aoqi@0 393 javaLang = c.owner()._package("java.lang");
aoqi@0 394
aoqi@0 395 // collate type names and identifiers to determine which types can be imported
aoqi@0 396 for( ReferenceList tl : collectedReferences.values() ) {
aoqi@0 397 if(!tl.collisions(c) && !tl.isId()) {
aoqi@0 398 assert tl.getClasses().size() == 1;
aoqi@0 399
aoqi@0 400 // add to list of collected types
aoqi@0 401 importedClasses.add(tl.getClasses().get(0));
aoqi@0 402 }
aoqi@0 403 }
aoqi@0 404
aoqi@0 405 // the class itself that we will be generating is always accessible
aoqi@0 406 importedClasses.add(c);
aoqi@0 407
aoqi@0 408 // then print the declaration
aoqi@0 409 mode = Mode.PRINTING;
aoqi@0 410
aoqi@0 411 assert c.parentContainer().isPackage() : "this method is only for a pacakge-level class";
aoqi@0 412 JPackage pkg = (JPackage) c.parentContainer();
aoqi@0 413 if (!pkg.isUnnamed()) {
aoqi@0 414 nl().d(pkg);
aoqi@0 415 nl();
aoqi@0 416 }
aoqi@0 417
aoqi@0 418 // generate import statements
aoqi@0 419 JClass[] imports = importedClasses.toArray(new JClass[importedClasses.size()]);
aoqi@0 420 Arrays.sort(imports);
aoqi@0 421 for (JClass clazz : imports) {
aoqi@0 422 // suppress import statements for primitive types, built-in types,
aoqi@0 423 // types in the root package, and types in
aoqi@0 424 // the same package as the current type
aoqi@0 425 if(!supressImport(clazz, c)) {
aoqi@0 426 if (clazz instanceof JNarrowedClass) {
aoqi@0 427 clazz = clazz.erasure();
aoqi@0 428 }
aoqi@0 429
aoqi@0 430 p("import").p(clazz.fullName()).p(';').nl();
aoqi@0 431 }
aoqi@0 432 }
aoqi@0 433 nl();
aoqi@0 434
aoqi@0 435 d(c);
aoqi@0 436 }
aoqi@0 437
aoqi@0 438 /**
aoqi@0 439 * determine if an import statement should be supressed
aoqi@0 440 *
aoqi@0 441 * @param clazz JType that may or may not have an import
aoqi@0 442 * @param c JType that is the current class being processed
aoqi@0 443 * @return true if an import statement should be suppressed, false otherwise
aoqi@0 444 */
aoqi@0 445 private boolean supressImport(JClass clazz, JClass c) {
aoqi@0 446 if (clazz instanceof JNarrowedClass) {
aoqi@0 447 clazz = clazz.erasure();
aoqi@0 448 }
aoqi@0 449 if (clazz instanceof JAnonymousClass) {
aoqi@0 450 clazz = clazz._extends();
aoqi@0 451 }
aoqi@0 452
aoqi@0 453 if(clazz._package().isUnnamed())
aoqi@0 454 return true;
aoqi@0 455
aoqi@0 456 final String packageName = clazz._package().name();
aoqi@0 457 if(packageName.equals("java.lang"))
aoqi@0 458 return true; // no need to explicitly import java.lang classes
aoqi@0 459
aoqi@0 460 if (clazz._package() == c._package()){
aoqi@0 461 // inner classes require an import stmt.
aoqi@0 462 // All other pkg local classes do not need an
aoqi@0 463 // import stmt for ref.
aoqi@0 464 if(clazz.outer()==null) {
aoqi@0 465 return true; // no need to explicitly import a class into itself
aoqi@0 466 }
aoqi@0 467 }
aoqi@0 468 return false;
aoqi@0 469 }
aoqi@0 470
aoqi@0 471 private JPackage javaLang;
aoqi@0 472
aoqi@0 473
aoqi@0 474
aoqi@0 475 /**
aoqi@0 476 * Special character token we use to differenciate '>' as an operator and
aoqi@0 477 * '>' as the end of the type arguments. The former uses '>' and it requires
aoqi@0 478 * a preceding whitespace. The latter uses this, and it does not have a preceding
aoqi@0 479 * whitespace.
aoqi@0 480 */
aoqi@0 481 /*package*/ static final char CLOSE_TYPE_ARGS = '\uFFFF';
aoqi@0 482
aoqi@0 483 /**
aoqi@0 484 * Used during the optimization of class imports.
aoqi@0 485 *
aoqi@0 486 * List of {@link JClass}es whose short name is the same.
aoqi@0 487 *
aoqi@0 488 * @author Ryan.Shoemaker@Sun.COM
aoqi@0 489 */
aoqi@0 490 final class ReferenceList {
aoqi@0 491 private final ArrayList<JClass> classes = new ArrayList<JClass>();
aoqi@0 492
aoqi@0 493 /** true if this name is used as an identifier (like a variable name.) **/
aoqi@0 494 private boolean id;
aoqi@0 495
aoqi@0 496 /**
aoqi@0 497 * Returns true if the symbol represented by the short name
aoqi@0 498 * is "importable".
aoqi@0 499 */
aoqi@0 500 public boolean collisions(JDefinedClass enclosingClass) {
aoqi@0 501 // special case where a generated type collides with a type in package java
aoqi@0 502
aoqi@0 503 // more than one type with the same name
aoqi@0 504 if(classes.size() > 1)
aoqi@0 505 return true;
aoqi@0 506
aoqi@0 507 // an id and (at least one) type with the same name
aoqi@0 508 if(id && classes.size() != 0)
aoqi@0 509 return true;
aoqi@0 510
aoqi@0 511 for(JClass c : classes) {
aoqi@0 512 if (c instanceof JAnonymousClass) {
aoqi@0 513 c = c._extends();
aoqi@0 514 }
aoqi@0 515 if(c._package()==javaLang) {
aoqi@0 516 // make sure that there's no other class with this name within the same package
aoqi@0 517 Iterator<JDefinedClass> itr = enclosingClass._package().classes();
aoqi@0 518 while(itr.hasNext()) {
aoqi@0 519 // even if this is the only "String" class we use,
aoqi@0 520 // if the class called "String" is in the same package,
aoqi@0 521 // we still need to import it.
aoqi@0 522 JDefinedClass n = itr.next();
aoqi@0 523 if(n.name().equals(c.name()))
aoqi@0 524 return true; //collision
aoqi@0 525 }
aoqi@0 526 }
aoqi@0 527 if(c.outer()!=null)
aoqi@0 528 return true; // avoid importing inner class to work around 6431987. Also see jaxb issue 166
aoqi@0 529 }
aoqi@0 530
aoqi@0 531 return false;
aoqi@0 532 }
aoqi@0 533
aoqi@0 534 public void add(JClass clazz) {
aoqi@0 535 if(!classes.contains(clazz))
aoqi@0 536 classes.add(clazz);
aoqi@0 537 }
aoqi@0 538
aoqi@0 539 public List<JClass> getClasses() {
aoqi@0 540 return classes;
aoqi@0 541 }
aoqi@0 542
aoqi@0 543 public void setId(boolean value) {
aoqi@0 544 id = value;
aoqi@0 545 }
aoqi@0 546
aoqi@0 547 /**
aoqi@0 548 * Return true iff this is strictly an id, meaning that there
aoqi@0 549 * are no collisions with type names.
aoqi@0 550 */
aoqi@0 551 public boolean isId() {
aoqi@0 552 return id && classes.size() == 0;
aoqi@0 553 }
aoqi@0 554 }
aoqi@0 555 }

mercurial