1/* 2 * Copyright (c) 1999, 2004, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25/* 26 * COMPONENT_NAME: idl.toJava 27 * 28 * ORIGINS: 27 29 * 30 * Licensed Materials - Property of IBM 31 * 5639-D57 (C) COPYRIGHT International Business Machines Corp. 1997, 1999 32 * RMI-IIOP v1.0 33 * 34 */ 35 36package com.sun.tools.corba.se.idl.toJavaPortable; 37 38// NOTES: 39// -F46082.51<daz> Remove -stateful feature. 40// -D57118 <klr> Fix "narrow" in helper for abstract interface 41// -D58889 <klr> re-Fix "narrow" in helper for abstract interface 42// -D59383 <klr> 'get_class' in value helper returns value class, not helper. 43// -D59413 <klr> Remove Helper interface references for non-value types. 44// -D59435 <klr> Remove read_Object, write_Object completely. 45// -D59418 <klr> Move read_Value, write_Value to generator's helperRead. 46 47import java.io.PrintWriter; 48 49import java.util.Enumeration; 50import java.util.Vector; 51 52import com.sun.tools.corba.se.idl.GenFileStream; 53import com.sun.tools.corba.se.idl.InterfaceEntry; 54import com.sun.tools.corba.se.idl.MethodEntry; 55import com.sun.tools.corba.se.idl.ParameterEntry; 56import com.sun.tools.corba.se.idl.SymtabEntry; 57import com.sun.tools.corba.se.idl.ValueEntry; 58import com.sun.tools.corba.se.idl.ValueBoxEntry; 59import com.sun.tools.corba.se.idl.TypedefEntry; 60import com.sun.tools.corba.se.idl.InterfaceState; 61import com.sun.tools.corba.se.idl.PrimitiveEntry; 62import com.sun.tools.corba.se.idl.StructEntry; 63 64/** 65 * 66 **/ 67public class Helper implements AuxGen 68{ 69 /** 70 * Public zero-argument constructor. 71 **/ 72 public Helper () 73 { 74 } // ctor 75 76 /** 77 * Generate the helper class. Provides general algorithm 78 * for auxiliary binding generation: 79 * 80 * 1.) Initialize symbol table and symbol table entry members, 81 * common to all generators. 82 * 2.) Initialize members unique to this generator. 83 * 3.) Open print stream 84 * 4.) Write class heading: package, prologue, class statement, open curly 85 * 5.) Write class body: member data and methods 86 * 6.) Write class closing: close curly 87 * 7.) Close the print stream 88 **/ 89 public void generate (java.util.Hashtable symbolTable, com.sun.tools.corba.se.idl.SymtabEntry entry) 90 { 91 this.symbolTable = symbolTable; 92 this.entry = entry; 93 init (); 94 95 openStream (); 96 if (stream == null) 97 return; 98 writeHeading (); 99 writeBody (); 100 writeClosing (); 101 closeStream (); 102 } // generate 103 104 /** 105 * Initialize variables unique to this generator. 106 **/ 107 protected void init () 108 { 109 helperClass = entry.name () + "Helper"; 110 if (entry instanceof ValueBoxEntry) 111 { 112 ValueBoxEntry v = (ValueBoxEntry) entry; 113 TypedefEntry member = ((InterfaceState) v.state ().elementAt (0)).entry; 114 SymtabEntry mType = member.type (); 115 116 if (mType instanceof PrimitiveEntry) 117 helperType = Util.javaName (entry); 118 else 119 helperType = Util.javaName (mType); 120 } 121 else 122 helperType = Util.javaName (entry); 123 } // init 124 125 /** 126 * Open the print stream for subsequent output. 127 **/ 128 protected void openStream () 129 { 130 stream = Util.stream (entry, "Helper.java"); 131 } // openStream 132 133 /** 134 * Generate the heading, including package, imports, class statements, 135 * and open curly. 136 **/ 137 protected void writeHeading () 138 { 139 Util.writePackage (stream, entry, Util.HelperFile); 140 Util.writeProlog (stream, stream.name ()); 141 142 // Transfer comment to target <30jul1997daz>. 143 if (entry.comment () != null) 144 entry.comment ().generate ("", stream); 145 146 stream.print ("public final class " + helperClass); 147 if (entry instanceof ValueEntry) 148 stream.println (" implements org.omg.CORBA.portable.ValueHelper"); 149 else 150 stream.println (); 151 stream.println ('{'); 152 } 153 154 /** 155 * Generate members of this class. 156 **/ 157 protected void writeBody () 158 { 159 writeInstVars (); 160 writeCtors (); 161 writeInsert (); 162 writeExtract (); 163 writeType (); 164 writeID (); 165 writeRead (); 166 writeWrite (); 167 if (entry instanceof InterfaceEntry && !(entry instanceof ValueEntry)) { 168 writeNarrow (); 169 writeUncheckedNarrow (); 170 } 171 writeHelperInterface (); 172 if (entry instanceof ValueEntry) 173 writeValueHelperInterface (); 174 } // writeBody 175 176 /** 177 * Generate members of the Helper interface. 178 **/ 179 protected void writeHelperInterface () 180 { 181 } // writeHelperInterface 182 183 /** 184 * Generate members of the ValueHelper interface. 185 **/ 186 protected void writeValueHelperInterface () 187 { 188 writeGetID (); // moved for <d59413> 189 writeGetType (); // moved for <d59413> 190 writeGetInstance (); // not in ValueHelper interface 191 writeGetClass (); 192 writeGetSafeBaseIds (); 193 } // writeHelperInterface 194 195 /** 196 * Generate the closing statements. 197 **/ 198 protected void writeClosing () 199 { 200 stream.println ('}'); 201 } 202 203 /** 204 * Write the stream to file by closing the print stream. 205 **/ 206 protected void closeStream () 207 { 208 stream.close (); 209 } 210 211 /** 212 * Generate the instance variables. 213 **/ 214 protected void writeInstVars () 215 { 216 stream.println (" private static String _id = \"" + Util.stripLeadingUnderscoresFromID (entry.repositoryID ().ID ()) + "\";"); 217 if (entry instanceof ValueEntry) 218 { 219 stream.println (); 220 stream.println (" private static " + helperClass + " helper = new " + helperClass + " ();"); 221 stream.println (); 222 stream.println (" private static String[] _truncatable_ids = {"); 223 stream.print (" _id"); 224 225 // Any safe ValueEntry must have a concete value parent. 226 // The topmost parent cannot be safe since it doesn't have 227 // a concrete parent. 228 ValueEntry child = (ValueEntry) entry; 229 while (child.isSafe ()) 230 { 231 stream.println(","); 232 ValueEntry parent = (ValueEntry)child.derivedFrom ().elementAt (0); 233 stream.print(" \"" + Util.stripLeadingUnderscoresFromID (parent.repositoryID ().ID ()) + "\""); 234 child = parent; 235 } 236 stream.println(" };"); 237 } 238 stream.println (); 239 } // writeInstVars 240 241 /** 242 * Generate the constructors. 243 **/ 244 protected void writeCtors () 245 { 246 stream.println (" public " + helperClass + "()"); 247 stream.println (" {"); 248 stream.println (" }"); 249 stream.println (); 250 } // writeCtors 251 252 /** 253 * Generate the insert method. 254 **/ 255 protected void writeInsert () 256 { 257 stream.println (" public static void insert (org.omg.CORBA.Any a, " + helperType + " that)"); 258 stream.println (" {"); 259 stream.println (" org.omg.CORBA.portable.OutputStream out = a.create_output_stream ();"); 260 stream.println (" a.type (type ());"); 261 stream.println (" write (out, that);"); 262 stream.println (" a.read_value (out.create_input_stream (), type ());"); 263 stream.println (" }"); 264 stream.println (); 265 } // writeInsert 266 267 /** 268 * Generate the extract method. 269 **/ 270 protected void writeExtract () 271 { 272 stream.println (" public static " + helperType + " extract (org.omg.CORBA.Any a)"); 273 stream.println (" {"); 274 stream.println (" return read (a.create_input_stream ());"); 275 stream.println (" }"); 276 stream.println (); 277 } // writeExtract 278 279 /** 280 * Generate the typecode variable and type method. 281 **/ 282 protected void writeType () 283 { 284 boolean canRecurse = entry instanceof ValueEntry 285 || entry instanceof ValueBoxEntry 286 || entry instanceof StructEntry; 287 stream.println (" private static org.omg.CORBA.TypeCode __typeCode = null;"); 288 if (canRecurse) 289 stream.println (" private static boolean __active = false;"); 290 stream.println (" synchronized public static org.omg.CORBA.TypeCode type ()"); 291 stream.println (" {"); 292 stream.println (" if (__typeCode == null)"); 293 stream.println (" {"); 294 if (canRecurse) { 295 stream.println (" synchronized (org.omg.CORBA.TypeCode.class)"); 296 stream.println (" {"); 297 stream.println (" if (__typeCode == null)"); 298 stream.println (" {"); 299 stream.println (" if (__active)"); 300 stream.println (" {"); 301 stream.println (" return org.omg.CORBA.ORB.init().create_recursive_tc ( _id );"); 302 stream.println (" }"); 303 stream.println (" __active = true;"); 304 ((JavaGenerator)entry.generator ()).helperType (0, " ", new TCOffsets (), "__typeCode", entry, stream); 305 } 306 else 307 ((JavaGenerator)entry.generator ()).helperType (0, " ", new TCOffsets (), "__typeCode", entry, stream); 308 309 // Generate body of type() method 310 311 if (canRecurse) { 312 stream.println (" __active = false;"); 313 stream.println (" }"); 314 stream.println (" }"); 315 } 316 stream.println (" }"); 317 stream.println (" return __typeCode;"); 318 stream.println (" }"); 319 stream.println (); 320 } // writeType 321 322 /** 323 * Generate the ID method. 324 **/ 325 protected void writeID () 326 { 327 stream.println (" public static String id ()"); 328 stream.println (" {"); 329 stream.println (" return _id;"); 330 stream.println (" }"); 331 stream.println (); 332 } // writeID 333 334 /** 335 * Generate the read method. 336 **/ 337 protected void writeRead () 338 { 339 340 boolean isLocalInterface = false; 341 342 if (entry instanceof InterfaceEntry) { 343 InterfaceEntry ie = (InterfaceEntry) entry; 344 345 // for #pragma sun_local or sun_localservant, or actual local 346 // local interface, set the flag by checking on both 347 isLocalInterface = ie.isLocal() | ie.isLocalServant(); 348 } 349 350 stream.println (" public static " + helperType + " read (org.omg.CORBA.portable.InputStream istream)"); 351 stream.println (" {"); 352 if ( !isLocalInterface ) { // nonLocal Interface and other types 353 ((JavaGenerator)entry.generator ()).helperRead (helperType, entry, stream); 354 } else { //Local interface should throw exception 355 stream.println (" throw new org.omg.CORBA.MARSHAL ();"); 356 } 357 stream.println (" }"); 358 stream.println (); 359 } // writeRead 360 361 /** 362 * Generate the write method. 363 **/ 364 protected void writeWrite () 365 { 366 367 boolean isLocalInterface = false; 368 369 if (entry instanceof InterfaceEntry) { 370 InterfaceEntry ie = (InterfaceEntry) entry; 371 372 // for #pragma sun_local or sun_localservant, or actual local 373 // local interface, set the flag by checking on both 374 isLocalInterface = ie.isLocal() | ie.isLocalServant(); 375 } 376 377 stream.println (" public static void write (org.omg.CORBA.portable.OutputStream ostream, " + helperType + " value)"); 378 stream.println (" {"); 379 if ( !isLocalInterface ) { // nonLocal Interface and other types 380 ((JavaGenerator)entry.generator ()).helperWrite (entry, stream); 381 } else { //Local interface should throw exception 382 stream.println (" throw new org.omg.CORBA.MARSHAL ();"); 383 } 384 stream.println (" }"); 385 stream.println (); 386 } // writeWrite 387 388 389 /** 390 * Generate the narrow method. 391 **/ 392 protected void writeNarrow () 393 { 394 writeRemoteNarrow (); 395 stream.println (); 396 } 397 398 /** 399 * Write the narrow() method for a remotable object. 400 **/ 401 protected void writeRemoteNarrow () 402 { 403 InterfaceEntry ie = (InterfaceEntry) entry; 404 405 // narrow for LocalObject interface 406 if (ie.isLocal ()) { 407 writeRemoteNarrowForLocal (false); 408 return; 409 } 410 411 // narrow for Abstract interface 412 if (ie.isAbstract ()) { 413 writeRemoteNarrowForAbstract (false); 414 return; 415 } else { 416 // Determine if the non-abstract interface has any abstract parents 417 for (int i = 0; i < ie.derivedFrom ().size (); i++) { 418 SymtabEntry parent = (SymtabEntry) ie.derivedFrom ().elementAt (i); 419 if (((InterfaceEntry) parent).isAbstract ()) { 420 writeRemoteNarrowForAbstract (true); 421 break; 422 } 423 } 424 } 425 426 stream.println (" public static " + helperType + " narrow (org.omg.CORBA.Object obj)"); 427 stream.println (" {"); 428 stream.println (" if (obj == null)"); 429 stream.println (" return null;"); 430 stream.println (" else if (obj instanceof " + helperType + ')'); 431 stream.println (" return (" + helperType + ")obj;"); 432 stream.println (" else if (!obj._is_a (id ()))"); 433 stream.println (" throw new org.omg.CORBA.BAD_PARAM ();"); 434 stream.println (" else"); 435 stream.println (" {"); 436 stream.println (" org.omg.CORBA.portable.Delegate delegate = ((org.omg.CORBA.portable.ObjectImpl)obj)._get_delegate ();"); 437 String stubNameofEntry = stubName ((InterfaceEntry)entry); 438 stream.println (" " + stubNameofEntry + " stub = new " + stubNameofEntry + " ();"); 439 stream.println (" stub._set_delegate(delegate);"); 440 stream.println (" return stub;"); 441 stream.println (" }"); 442 stream.println (" }"); 443 } // writeRemoteNarrow 444 445 /** 446 * Write the narrow() method for local interface. 447 **/ 448 private void writeRemoteNarrowForLocal (boolean hasAbstractParent) 449 { 450 stream.println (" public static " + helperType + " narrow (org.omg.CORBA.Object obj)"); 451 stream.println (" {"); 452 stream.println (" if (obj == null)"); 453 stream.println (" return null;"); 454 stream.println (" else if (obj instanceof " + helperType + ')'); 455 stream.println (" return (" + helperType + ")obj;"); 456 stream.println (" else"); 457 stream.println (" throw new org.omg.CORBA.BAD_PARAM ();"); 458 stream.println (" }"); 459 } // writeRemoteNarrowForLocal 460 461 /** 462 * Write the narrow() method for abstract interface. 463 **/ 464 private void writeRemoteNarrowForAbstract (boolean hasAbstractParent) 465 { 466 stream.print (" public static " + helperType + " narrow (java.lang.Object obj)"); 467 stream.println (" {"); 468 stream.println (" if (obj == null)"); 469 stream.println (" return null;"); 470 if (hasAbstractParent) 471 { 472 stream.println (" else if (obj instanceof org.omg.CORBA.Object)"); 473 stream.println (" return narrow ((org.omg.CORBA.Object) obj);"); 474 } 475 else 476 { 477 stream.println (" else if (obj instanceof " + helperType + ')'); 478 stream.println (" return (" + helperType + ")obj;"); 479 } 480 481 // If hasAbstractParent is false, then THIS entry must be abstract. 482 // This method is also called in case THIS entry is not abstract, but 483 // there is an abstract parent. If this entry is not abstract, 484 // it can never narrow to a CORBA object reference. 485 if (!hasAbstractParent) { // <d58889> 486 String stubNameofEntry = stubName ((InterfaceEntry)entry); 487 488 stream.println (" else if ((obj instanceof org.omg.CORBA.portable.ObjectImpl) &&"); 489 stream.println (" (((org.omg.CORBA.Object)obj)._is_a (id ()))) {"); 490 stream.println (" org.omg.CORBA.portable.ObjectImpl impl = (org.omg.CORBA.portable.ObjectImpl)obj ;" ) ; 491 stream.println (" org.omg.CORBA.portable.Delegate delegate = impl._get_delegate() ;" ) ; 492 stream.println (" " + stubNameofEntry + " stub = new " + stubNameofEntry + " ();"); 493 stream.println (" stub._set_delegate(delegate);"); 494 stream.println (" return stub;" ) ; 495 stream.println (" }" ) ; 496 }; 497 // end <d57118 - check for remotable - klr> 498 499 stream.println (" throw new org.omg.CORBA.BAD_PARAM ();"); 500 stream.println (" }"); 501 stream.println (); 502 } // writeRemoteNarrowForAbstract 503 504 505 /** 506 * Generate the unchecked narrow method. 507 **/ 508 protected void writeUncheckedNarrow () 509 { 510 writeUncheckedRemoteNarrow (); 511 stream.println (); 512 } 513 514 /** 515 * Write the unchecked narrow() method for a remotable object. 516 **/ 517 protected void writeUncheckedRemoteNarrow () 518 { 519 InterfaceEntry ie = (InterfaceEntry) entry; 520 521 // unchecked narrow for LocalObject interface 522 if (ie.isLocal ()) { 523 writeRemoteUncheckedNarrowForLocal (false); 524 return; 525 } 526 527 // unchecked narrow for Abstract interface 528 if (ie.isAbstract ()) { 529 writeRemoteUncheckedNarrowForAbstract (false); 530 return; 531 } else { 532 // Determine if the non-abstract interface has any abstract parents 533 for (int i = 0; i < ie.derivedFrom ().size (); i++) { 534 SymtabEntry parent = (SymtabEntry) ie.derivedFrom ().elementAt (i); 535 if (((InterfaceEntry) parent).isAbstract ()) { 536 writeRemoteUncheckedNarrowForAbstract (true); 537 break; 538 } 539 } 540 } 541 542 stream.println (" public static " + helperType + " unchecked_narrow (org.omg.CORBA.Object obj)"); 543 stream.println (" {"); 544 stream.println (" if (obj == null)"); 545 stream.println (" return null;"); 546 stream.println (" else if (obj instanceof " + helperType + ')'); 547 stream.println (" return (" + helperType + ")obj;"); 548 stream.println (" else"); 549 stream.println (" {"); 550 stream.println (" org.omg.CORBA.portable.Delegate delegate = ((org.omg.CORBA.portable.ObjectImpl)obj)._get_delegate ();"); 551 String stubNameofEntry = stubName ((InterfaceEntry)entry); 552 stream.println (" " + stubNameofEntry + " stub = new " + stubNameofEntry + " ();"); 553 stream.println (" stub._set_delegate(delegate);"); 554 stream.println (" return stub;"); 555 stream.println (" }"); 556 stream.println (" }"); 557 } // writeUncheckedRemoteNarrow 558 559 /** 560 * Write the unchecked narrow() method for local interface. 561 **/ 562 private void writeRemoteUncheckedNarrowForLocal (boolean hasAbstractParent) 563 { 564 stream.println (" public static " + helperType + " unchecked_narrow (org.omg.CORBA.Object obj)"); 565 stream.println (" {"); 566 stream.println (" if (obj == null)"); 567 stream.println (" return null;"); 568 stream.println (" else if (obj instanceof " + helperType + ')'); 569 stream.println (" return (" + helperType + ")obj;"); 570 stream.println (" else"); 571 stream.println (" throw new org.omg.CORBA.BAD_PARAM ();"); 572 stream.println (" }"); 573 } // writeRemoteUncheckedNarrowForLocal 574 575 /** 576 * Write the unchecked narrow() method for abstract interface. 577 **/ 578 private void writeRemoteUncheckedNarrowForAbstract (boolean hasAbstractParent) 579 { 580 stream.print (" public static " + helperType + " unchecked_narrow (java.lang.Object obj)"); 581 stream.println (" {"); 582 stream.println (" if (obj == null)"); 583 stream.println (" return null;"); 584 if (hasAbstractParent) 585 { 586 stream.println (" else if (obj instanceof org.omg.CORBA.Object)"); 587 stream.println (" return unchecked_narrow ((org.omg.CORBA.Object) obj);"); 588 } 589 else 590 { 591 stream.println (" else if (obj instanceof " + helperType + ')'); 592 stream.println (" return (" + helperType + ")obj;"); 593 } 594 595 if (!hasAbstractParent) { 596 String stubNameofEntry = stubName ((InterfaceEntry)entry); 597 598 stream.println (" else if (obj instanceof org.omg.CORBA.portable.ObjectImpl) {"); 599 stream.println (" org.omg.CORBA.portable.ObjectImpl impl = (org.omg.CORBA.portable.ObjectImpl)obj ;" ) ; 600 stream.println (" org.omg.CORBA.portable.Delegate delegate = impl._get_delegate() ;" ) ; 601 stream.println (" " + stubNameofEntry + " stub = new " + stubNameofEntry + " ();"); 602 stream.println (" stub._set_delegate(delegate);"); 603 stream.println (" return stub;" ) ; 604 stream.println (" }" ) ; 605 }; 606 607 stream.println (" throw new org.omg.CORBA.BAD_PARAM ();"); 608 stream.println (" }"); 609 stream.println (); 610 } // writeRemoteUncheckedNarrowForAbstract 611 612 613 /** 614 * Generate the GetID method. 615 **/ 616 protected void writeGetID () 617 { 618 if ( !Util.IDLEntity (entry)) 619 return; 620 stream.println (" public String get_id ()"); 621 stream.println (" {"); 622 stream.println (" return _id;"); 623 stream.println (" }"); 624 stream.println (); 625 } // writeGetID 626 627 /** 628 * Generate the GetType method. 629 **/ 630 protected void writeGetType () 631 { 632 if ( !Util.IDLEntity (entry)) 633 return; 634 stream.println (" public org.omg.CORBA.TypeCode get_type ()"); 635 stream.println (" {"); 636 stream.println (" return type ();"); 637 stream.println (" }"); 638 stream.println (); 639 } // writeGetID 640 641 /** 642 * Generate the get_class method. 643 **/ 644 protected void writeGetClass () 645 { 646 stream.println (" public Class get_class ()"); 647 stream.println (" {"); 648 stream.println (" return " + helperType + ".class;"); //<d59383> 649 stream.println (" }"); 650 stream.println (); 651 } // writeGetClass 652 653 /** 654 * Generate the get_instance method. 655 **/ 656 protected void writeGetInstance () 657 { 658 stream.println (" public static org.omg.CORBA.portable.ValueHelper get_instance ()"); 659 stream.println (" {"); 660 stream.println (" return helper;"); 661 stream.println (" }"); 662 stream.println (); 663 } // writeGetInstance 664 665 /** 666 * Generate the GetSafeBaseIds method. 667 **/ 668 protected void writeGetSafeBaseIds () 669 { 670 stream.println (" public String[] get_truncatable_base_ids ()"); 671 stream.println (" {"); 672 stream.println (" return _truncatable_ids;"); 673 stream.println (" }"); 674 stream.println (); 675 } // writeGetSafeBaseIds 676 677 /** 678 * Return the stub name for the interface entry. 679 **/ 680 protected String stubName (InterfaceEntry entry) 681 { 682 String name; 683 if (entry.container ().name ().equals ("")) 684 name = '_' + entry.name () + "Stub"; 685 else 686 { 687 name = Util.containerFullName (entry.container ()) + "._" + entry.name () + "Stub"; 688 } 689 return name.replace ('/', '.'); 690 } // stubName 691 692 protected java.util.Hashtable symbolTable; 693 protected com.sun.tools.corba.se.idl.SymtabEntry entry; 694 protected GenFileStream stream; 695 696 // Unique to this generator 697 protected String helperClass; 698 protected String helperType; 699} // class Helper 700