0N/A/*
2362N/A * Copyright (c) 2000, 2003, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/Apackage javax.print;
0N/A
0N/Aimport java.io.Serializable;
0N/A
0N/Aimport java.util.AbstractMap;
0N/Aimport java.util.AbstractSet;
0N/Aimport java.util.Iterator;
0N/Aimport java.util.Map;
0N/Aimport java.util.NoSuchElementException;
0N/Aimport java.util.Set;
0N/Aimport java.util.Vector;
0N/A
0N/A/**
0N/A * Class MimeType encapsulates a Multipurpose Internet Mail Extensions (MIME)
0N/A * media type as defined in <A HREF="http://www.ietf.org/rfc/rfc2045.txt">RFC
0N/A * 2045</A> and <A HREF="http://www.ietf.org/rfc/rfc2046.txt">RFC 2046</A>. A
0N/A * MIME type object is part of a {@link DocFlavor DocFlavor} object and
0N/A * specifies the format of the print data.
0N/A * <P>
0N/A * Class MimeType is similar to the like-named
0N/A * class in package {@link java.awt.datatransfer java.awt.datatransfer}. Class
0N/A * java.awt.datatransfer.MimeType is not used in the Jini Print Service API
0N/A * for two reasons:
0N/A * <OL TYPE=1>
0N/A * <LI>
0N/A * Since not all Java profiles include the AWT, the Jini Print Service should
0N/A * not depend on an AWT class.
0N/A * <P>
0N/A * <LI>
0N/A * The implementation of class java.awt.datatransfer.MimeType does not
0N/A * guarantee
0N/A * that equivalent MIME types will have the same serialized representation.
0N/A * Thus, since the Jini Lookup Service (JLUS) matches service attributes based
0N/A * on equality of serialized representations, JLUS searches involving MIME
0N/A * types encapsulated in class java.awt.datatransfer.MimeType may incorrectly
0N/A * fail to match.
0N/A * </OL>
0N/A * <P>
0N/A * Class MimeType's serialized representation is based on the following
0N/A * canonical form of a MIME type string. Thus, two MIME types that are not
0N/A * identical but that are equivalent (that have the same canonical form) will
0N/A * be considered equal by the JLUS's matching algorithm.
0N/A * <UL>
0N/A * <LI> The media type, media subtype, and parameters are retained, but all
0N/A * comments and whitespace characters are discarded.
0N/A * <LI> The media type, media subtype, and parameter names are converted to
0N/A * lowercase.
0N/A * <LI> The parameter values retain their original case, except a charset
0N/A * parameter value for a text media type is converted to lowercase.
0N/A * <LI> Quote characters surrounding parameter values are removed.
0N/A * <LI> Quoting backslash characters inside parameter values are removed.
0N/A * <LI> The parameters are arranged in ascending order of parameter name.
0N/A * </UL>
0N/A * <P>
0N/A *
0N/A * @author Alan Kaminsky
0N/A */
0N/Aclass MimeType implements Serializable, Cloneable {
0N/A
0N/A private static final long serialVersionUID = -2785720609362367683L;
0N/A
0N/A /**
0N/A * Array of strings that hold pieces of this MIME type's canonical form.
0N/A * If the MIME type has <I>n</I> parameters, <I>n</I> &gt;= 0, then the
0N/A * strings in the array are:
0N/A * <BR>Index 0 -- Media type.
0N/A * <BR>Index 1 -- Media subtype.
0N/A * <BR>Index 2<I>i</I>+2 -- Name of parameter <I>i</I>,
0N/A * <I>i</I>=0,1,...,<I>n</I>-1.
0N/A * <BR>Index 2<I>i</I>+3 -- Value of parameter <I>i</I>,
0N/A * <I>i</I>=0,1,...,<I>n</I>-1.
0N/A * <BR>Parameters are arranged in ascending order of parameter name.
0N/A * @serial
0N/A */
0N/A private String[] myPieces;
0N/A
0N/A /**
0N/A * String value for this MIME type. Computed when needed and cached.
0N/A */
0N/A private transient String myStringValue = null;
0N/A
0N/A /**
0N/A * Parameter map entry set. Computed when needed and cached.
0N/A */
0N/A private transient ParameterMapEntrySet myEntrySet = null;
0N/A
0N/A /**
0N/A * Parameter map. Computed when needed and cached.
0N/A */
0N/A private transient ParameterMap myParameterMap = null;
0N/A
0N/A /**
0N/A * Parameter map entry.
0N/A */
0N/A private class ParameterMapEntry implements Map.Entry {
0N/A private int myIndex;
0N/A public ParameterMapEntry(int theIndex) {
0N/A myIndex = theIndex;
0N/A }
0N/A public Object getKey(){
0N/A return myPieces[myIndex];
0N/A }
0N/A public Object getValue(){
0N/A return myPieces[myIndex+1];
0N/A }
0N/A public Object setValue (Object value) {
0N/A throw new UnsupportedOperationException();
0N/A }
0N/A public boolean equals(Object o) {
0N/A return (o != null &&
0N/A o instanceof Map.Entry &&
0N/A getKey().equals (((Map.Entry) o).getKey()) &&
0N/A getValue().equals(((Map.Entry) o).getValue()));
0N/A }
0N/A public int hashCode() {
0N/A return getKey().hashCode() ^ getValue().hashCode();
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Parameter map entry set iterator.
0N/A */
0N/A private class ParameterMapEntrySetIterator implements Iterator {
0N/A private int myIndex = 2;
0N/A public boolean hasNext() {
0N/A return myIndex < myPieces.length;
0N/A }
0N/A public Object next() {
0N/A if (hasNext()) {
0N/A ParameterMapEntry result = new ParameterMapEntry (myIndex);
0N/A myIndex += 2;
0N/A return result;
0N/A } else {
0N/A throw new NoSuchElementException();
0N/A }
0N/A }
0N/A public void remove() {
0N/A throw new UnsupportedOperationException();
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Parameter map entry set.
0N/A */
0N/A private class ParameterMapEntrySet extends AbstractSet {
0N/A public Iterator iterator() {
0N/A return new ParameterMapEntrySetIterator();
0N/A }
0N/A public int size() {
0N/A return (myPieces.length - 2) / 2;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Parameter map.
0N/A */
0N/A private class ParameterMap extends AbstractMap {
0N/A public Set entrySet() {
0N/A if (myEntrySet == null) {
0N/A myEntrySet = new ParameterMapEntrySet();
0N/A }
0N/A return myEntrySet;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Construct a new MIME type object from the given string. The given
0N/A * string is converted into canonical form and stored internally.
0N/A *
0N/A * @param s MIME media type string.
0N/A *
0N/A * @exception NullPointerException
0N/A * (unchecked exception) Thrown if <CODE>s</CODE> is null.
0N/A * @exception IllegalArgumentException
0N/A * (unchecked exception) Thrown if <CODE>s</CODE> does not obey the
0N/A * syntax for a MIME media type string.
0N/A */
0N/A public MimeType(String s) {
0N/A parse (s);
0N/A }
0N/A
0N/A /**
0N/A * Returns this MIME type object's MIME type string based on the canonical
0N/A * form. Each parameter value is enclosed in quotes.
0N/A */
0N/A public String getMimeType() {
0N/A return getStringValue();
0N/A }
0N/A
0N/A /**
0N/A * Returns this MIME type object's media type.
0N/A */
0N/A public String getMediaType() {
0N/A return myPieces[0];
0N/A }
0N/A
0N/A /**
0N/A * Returns this MIME type object's media subtype.
0N/A */
0N/A public String getMediaSubtype() {
0N/A return myPieces[1];
0N/A }
0N/A
0N/A /**
0N/A * Returns an unmodifiable map view of the parameters in this MIME type
0N/A * object. Each entry in the parameter map view consists of a parameter
0N/A * name String (key) mapping to a parameter value String. If this MIME
0N/A * type object has no parameters, an empty map is returned.
0N/A *
0N/A * @return Parameter map for this MIME type object.
0N/A */
0N/A public Map getParameterMap() {
0N/A if (myParameterMap == null) {
0N/A myParameterMap = new ParameterMap();
0N/A }
0N/A return myParameterMap;
0N/A }
0N/A
0N/A /**
0N/A * Converts this MIME type object to a string.
0N/A *
0N/A * @return MIME type string based on the canonical form. Each parameter
0N/A * value is enclosed in quotes.
0N/A */
0N/A public String toString() {
0N/A return getStringValue();
0N/A }
0N/A
0N/A /**
0N/A * Returns a hash code for this MIME type object.
0N/A */
0N/A public int hashCode() {
0N/A return getStringValue().hashCode();
0N/A }
0N/A
0N/A /**
0N/A * Determine if this MIME type object is equal to the given object. The two
0N/A * are equal if the given object is not null, is an instance of class
0N/A * net.jini.print.data.MimeType, and has the same canonical form as this
0N/A * MIME type object (that is, has the same type, subtype, and parameters).
0N/A * Thus, if two MIME type objects are the same except for comments, they are
0N/A * considered equal. However, "text/plain" and "text/plain;
0N/A * charset=us-ascii" are not considered equal, even though they represent
0N/A * the same media type (because the default character set for plain text is
0N/A * US-ASCII).
0N/A *
0N/A * @param obj Object to test.
0N/A *
0N/A * @return True if this MIME type object equals <CODE>obj</CODE>, false
0N/A * otherwise.
0N/A */
0N/A public boolean equals (Object obj) {
0N/A return(obj != null &&
0N/A obj instanceof MimeType &&
0N/A getStringValue().equals(((MimeType) obj).getStringValue()));
0N/A }
0N/A
0N/A /**
0N/A * Returns this MIME type's string value in canonical form.
0N/A */
0N/A private String getStringValue() {
0N/A if (myStringValue == null) {
0N/A StringBuffer result = new StringBuffer();
0N/A result.append (myPieces[0]);
0N/A result.append ('/');
0N/A result.append (myPieces[1]);
0N/A int n = myPieces.length;
0N/A for (int i = 2; i < n; i += 2) {
0N/A result.append(';');
0N/A result.append(' ');
0N/A result.append(myPieces[i]);
0N/A result.append('=');
0N/A result.append(addQuotes (myPieces[i+1]));
0N/A }
0N/A myStringValue = result.toString();
0N/A }
0N/A return myStringValue;
0N/A }
0N/A
0N/A// Hidden classes, constants, and operations for parsing a MIME media type
0N/A// string.
0N/A
0N/A // Lexeme types.
0N/A private static final int TOKEN_LEXEME = 0;
0N/A private static final int QUOTED_STRING_LEXEME = 1;
0N/A private static final int TSPECIAL_LEXEME = 2;
0N/A private static final int EOF_LEXEME = 3;
0N/A private static final int ILLEGAL_LEXEME = 4;
0N/A
0N/A // Class for a lexical analyzer.
0N/A private static class LexicalAnalyzer {
0N/A protected String mySource;
0N/A protected int mySourceLength;
0N/A protected int myCurrentIndex;
0N/A protected int myLexemeType;
0N/A protected int myLexemeBeginIndex;
0N/A protected int myLexemeEndIndex;
0N/A
0N/A public LexicalAnalyzer(String theSource) {
0N/A mySource = theSource;
0N/A mySourceLength = theSource.length();
0N/A myCurrentIndex = 0;
0N/A nextLexeme();
0N/A }
0N/A
0N/A public int getLexemeType() {
0N/A return myLexemeType;
0N/A }
0N/A
0N/A public String getLexeme() {
0N/A return(myLexemeBeginIndex >= mySourceLength ?
0N/A null :
0N/A mySource.substring(myLexemeBeginIndex, myLexemeEndIndex));
0N/A }
0N/A
0N/A public char getLexemeFirstCharacter() {
0N/A return(myLexemeBeginIndex >= mySourceLength ?
0N/A '\u0000' :
0N/A mySource.charAt(myLexemeBeginIndex));
0N/A }
0N/A
0N/A public void nextLexeme() {
0N/A int state = 0;
0N/A int commentLevel = 0;
0N/A char c;
0N/A while (state >= 0) {
0N/A switch (state) {
0N/A // Looking for a token, quoted string, or tspecial
0N/A case 0:
0N/A if (myCurrentIndex >= mySourceLength) {
0N/A myLexemeType = EOF_LEXEME;
0N/A myLexemeBeginIndex = mySourceLength;
0N/A myLexemeEndIndex = mySourceLength;
0N/A state = -1;
0N/A } else if (Character.isWhitespace
0N/A (c = mySource.charAt (myCurrentIndex ++))) {
0N/A state = 0;
0N/A } else if (c == '\"') {
0N/A myLexemeType = QUOTED_STRING_LEXEME;
0N/A myLexemeBeginIndex = myCurrentIndex;
0N/A state = 1;
0N/A } else if (c == '(') {
0N/A ++ commentLevel;
0N/A state = 3;
0N/A } else if (c == '/' || c == ';' || c == '=' ||
0N/A c == ')' || c == '<' || c == '>' ||
0N/A c == '@' || c == ',' || c == ':' ||
0N/A c == '\\' || c == '[' || c == ']' ||
0N/A c == '?') {
0N/A myLexemeType = TSPECIAL_LEXEME;
0N/A myLexemeBeginIndex = myCurrentIndex - 1;
0N/A myLexemeEndIndex = myCurrentIndex;
0N/A state = -1;
0N/A } else {
0N/A myLexemeType = TOKEN_LEXEME;
0N/A myLexemeBeginIndex = myCurrentIndex - 1;
0N/A state = 5;
0N/A }
0N/A break;
0N/A // In a quoted string
0N/A case 1:
0N/A if (myCurrentIndex >= mySourceLength) {
0N/A myLexemeType = ILLEGAL_LEXEME;
0N/A myLexemeBeginIndex = mySourceLength;
0N/A myLexemeEndIndex = mySourceLength;
0N/A state = -1;
0N/A } else if ((c = mySource.charAt (myCurrentIndex ++)) == '\"') {
0N/A myLexemeEndIndex = myCurrentIndex - 1;
0N/A state = -1;
0N/A } else if (c == '\\') {
0N/A state = 2;
0N/A } else {
0N/A state = 1;
0N/A }
0N/A break;
0N/A // In a quoted string, backslash seen
0N/A case 2:
0N/A if (myCurrentIndex >= mySourceLength) {
0N/A myLexemeType = ILLEGAL_LEXEME;
0N/A myLexemeBeginIndex = mySourceLength;
0N/A myLexemeEndIndex = mySourceLength;
0N/A state = -1;
0N/A } else {
0N/A ++ myCurrentIndex;
0N/A state = 1;
0N/A } break;
0N/A // In a comment
0N/A case 3: if (myCurrentIndex >= mySourceLength) {
0N/A myLexemeType = ILLEGAL_LEXEME;
0N/A myLexemeBeginIndex = mySourceLength;
0N/A myLexemeEndIndex = mySourceLength;
0N/A state = -1;
0N/A } else if ((c = mySource.charAt (myCurrentIndex ++)) == '(') {
0N/A ++ commentLevel;
0N/A state = 3;
0N/A } else if (c == ')') {
0N/A -- commentLevel;
0N/A state = commentLevel == 0 ? 0 : 3;
0N/A } else if (c == '\\') {
0N/A state = 4;
0N/A } else { state = 3;
0N/A }
0N/A break;
0N/A // In a comment, backslash seen
0N/A case 4:
0N/A if (myCurrentIndex >= mySourceLength) {
0N/A myLexemeType = ILLEGAL_LEXEME;
0N/A myLexemeBeginIndex = mySourceLength;
0N/A myLexemeEndIndex = mySourceLength;
0N/A state = -1;
0N/A } else {
0N/A ++ myCurrentIndex;
0N/A state = 3;
0N/A }
0N/A break;
0N/A // In a token
0N/A case 5:
0N/A if (myCurrentIndex >= mySourceLength) {
0N/A myLexemeEndIndex = myCurrentIndex;
0N/A state = -1;
0N/A } else if (Character.isWhitespace
0N/A (c = mySource.charAt (myCurrentIndex ++))) {
0N/A myLexemeEndIndex = myCurrentIndex - 1;
0N/A state = -1;
0N/A } else if (c == '\"' || c == '(' || c == '/' ||
0N/A c == ';' || c == '=' || c == ')' ||
0N/A c == '<' || c == '>' || c == '@' ||
0N/A c == ',' || c == ':' || c == '\\' ||
0N/A c == '[' || c == ']' || c == '?') {
0N/A -- myCurrentIndex;
0N/A myLexemeEndIndex = myCurrentIndex;
0N/A state = -1;
0N/A } else {
0N/A state = 5;
0N/A }
0N/A break;
0N/A }
0N/A }
0N/A
0N/A }
0N/A
0N/A }
0N/A
0N/A /**
0N/A * Returns a lowercase version of the given string. The lowercase version
0N/A * is constructed by applying Character.toLowerCase() to each character of
0N/A * the given string, which maps characters to lowercase using the rules of
0N/A * Unicode. This mapping is the same regardless of locale, whereas the
0N/A * mapping of String.toLowerCase() may be different depending on the
0N/A * default locale.
0N/A */
0N/A private static String toUnicodeLowerCase(String s) {
0N/A int n = s.length();
0N/A char[] result = new char [n];
0N/A for (int i = 0; i < n; ++ i) {
0N/A result[i] = Character.toLowerCase (s.charAt (i));
0N/A }
0N/A return new String (result);
0N/A }
0N/A
0N/A /**
0N/A * Returns a version of the given string with backslashes removed.
0N/A */
0N/A private static String removeBackslashes(String s) {
0N/A int n = s.length();
0N/A char[] result = new char [n];
0N/A int i;
0N/A int j = 0;
0N/A char c;
0N/A for (i = 0; i < n; ++ i) {
0N/A c = s.charAt (i);
0N/A if (c == '\\') {
0N/A c = s.charAt (++ i);
0N/A }
0N/A result[j++] = c;
0N/A }
0N/A return new String (result, 0, j);
0N/A }
0N/A
0N/A /**
0N/A * Returns a version of the string surrounded by quotes and with interior
0N/A * quotes preceded by a backslash.
0N/A */
0N/A private static String addQuotes(String s) {
0N/A int n = s.length();
0N/A int i;
0N/A char c;
0N/A StringBuffer result = new StringBuffer (n+2);
0N/A result.append ('\"');
0N/A for (i = 0; i < n; ++ i) {
0N/A c = s.charAt (i);
0N/A if (c == '\"') {
0N/A result.append ('\\');
0N/A }
0N/A result.append (c);
0N/A }
0N/A result.append ('\"');
0N/A return result.toString();
0N/A }
0N/A
0N/A /**
0N/A * Parses the given string into canonical pieces and stores the pieces in
0N/A * {@link #myPieces <CODE>myPieces</CODE>}.
0N/A * <P>
0N/A * Special rules applied:
0N/A * <UL>
0N/A * <LI> If the media type is text, the value of a charset parameter is
0N/A * converted to lowercase.
0N/A * </UL>
0N/A *
0N/A * @param s MIME media type string.
0N/A *
0N/A * @exception NullPointerException
0N/A * (unchecked exception) Thrown if <CODE>s</CODE> is null.
0N/A * @exception IllegalArgumentException
0N/A * (unchecked exception) Thrown if <CODE>s</CODE> does not obey the
0N/A * syntax for a MIME media type string.
0N/A */
0N/A private void parse(String s) {
0N/A // Initialize.
0N/A if (s == null) {
0N/A throw new NullPointerException();
0N/A }
0N/A LexicalAnalyzer theLexer = new LexicalAnalyzer (s);
0N/A int theLexemeType;
0N/A Vector thePieces = new Vector();
0N/A boolean mediaTypeIsText = false;
0N/A boolean parameterNameIsCharset = false;
0N/A
0N/A // Parse media type.
0N/A if (theLexer.getLexemeType() == TOKEN_LEXEME) {
0N/A String mt = toUnicodeLowerCase (theLexer.getLexeme());
0N/A thePieces.add (mt);
0N/A theLexer.nextLexeme();
0N/A mediaTypeIsText = mt.equals ("text");
0N/A } else {
0N/A throw new IllegalArgumentException();
0N/A }
0N/A // Parse slash.
0N/A if (theLexer.getLexemeType() == TSPECIAL_LEXEME &&
0N/A theLexer.getLexemeFirstCharacter() == '/') {
0N/A theLexer.nextLexeme();
0N/A } else {
0N/A throw new IllegalArgumentException();
0N/A }
0N/A if (theLexer.getLexemeType() == TOKEN_LEXEME) {
0N/A thePieces.add (toUnicodeLowerCase (theLexer.getLexeme()));
0N/A theLexer.nextLexeme();
0N/A } else {
0N/A throw new IllegalArgumentException();
0N/A }
0N/A // Parse zero or more parameters.
0N/A while (theLexer.getLexemeType() == TSPECIAL_LEXEME &&
0N/A theLexer.getLexemeFirstCharacter() == ';') {
0N/A // Parse semicolon.
0N/A theLexer.nextLexeme();
0N/A
0N/A // Parse parameter name.
0N/A if (theLexer.getLexemeType() == TOKEN_LEXEME) {
0N/A String pn = toUnicodeLowerCase (theLexer.getLexeme());
0N/A thePieces.add (pn);
0N/A theLexer.nextLexeme();
0N/A parameterNameIsCharset = pn.equals ("charset");
0N/A } else {
0N/A throw new IllegalArgumentException();
0N/A }
0N/A
0N/A // Parse equals.
0N/A if (theLexer.getLexemeType() == TSPECIAL_LEXEME &&
0N/A theLexer.getLexemeFirstCharacter() == '=') {
0N/A theLexer.nextLexeme();
0N/A } else {
0N/A throw new IllegalArgumentException();
0N/A }
0N/A
0N/A // Parse parameter value.
0N/A if (theLexer.getLexemeType() == TOKEN_LEXEME) {
0N/A String pv = theLexer.getLexeme();
0N/A thePieces.add(mediaTypeIsText && parameterNameIsCharset ?
0N/A toUnicodeLowerCase (pv) :
0N/A pv);
0N/A theLexer.nextLexeme();
0N/A } else if (theLexer.getLexemeType() == QUOTED_STRING_LEXEME) {
0N/A String pv = removeBackslashes (theLexer.getLexeme());
0N/A thePieces.add(mediaTypeIsText && parameterNameIsCharset ?
0N/A toUnicodeLowerCase (pv) :
0N/A pv);
0N/A theLexer.nextLexeme();
0N/A } else {
0N/A throw new IllegalArgumentException();
0N/A }
0N/A }
0N/A
0N/A // Make sure we've consumed everything.
0N/A if (theLexer.getLexemeType() != EOF_LEXEME) {
0N/A throw new IllegalArgumentException();
0N/A }
0N/A
0N/A // Save the pieces. Parameters are not in ascending order yet.
0N/A int n = thePieces.size();
0N/A myPieces = (String[]) thePieces.toArray (new String [n]);
0N/A
0N/A // Sort the parameters into ascending order using an insertion sort.
0N/A int i, j;
0N/A String temp;
0N/A for (i = 4; i < n; i += 2) {
0N/A j = 2;
0N/A while (j < i && myPieces[j].compareTo (myPieces[i]) <= 0) {
0N/A j += 2;
0N/A }
0N/A while (j < i) {
0N/A temp = myPieces[j];
0N/A myPieces[j] = myPieces[i];
0N/A myPieces[i] = temp;
0N/A temp = myPieces[j+1];
0N/A myPieces[j+1] = myPieces[i+1];
0N/A myPieces[i+1] = temp;
0N/A j += 2;
0N/A }
0N/A }
0N/A }
0N/A}