package de.axone.web; import java.io.UnsupportedEncodingException; import java.net.MalformedURLException; import java.net.URI; import java.net.URL; import java.net.URLConnection; import java.net.URLDecoder; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.EnumMap; import java.util.EnumSet; import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.Vector; import java.util.function.Function; import javax.servlet.http.HttpServletRequest; import de.axone.data.Charsets; import de.axone.data.Label; import de.axone.tools.Str; import de.axone.web.SuperURLBuilders.SuperURLBuilder_Copy; import de.axone.web.SuperURLBuilders.SuperURLBuilder_Request; import de.axone.web.SuperURLBuilders.SuperURLBuilder_String; import de.axone.web.SuperURLBuilders.SuperURLBuilder_URI; /** * Extended and more usefull version of Javas URI class whereas * it uses URI wherever possible to do de/encoding stuff but * privides a wider range of manipulation functions. (Setters!!!) * * A URI looks like this: * * scheme://user:pass@host:port/path?query#fragment * * scheme: e.g. http * user: the user * pass: the pass or nothing * host: www.subdomain.myhost.com * port: 80. mostly nothing * path: blah/blo/blu/bläh * query: key1=value1&key2=value2;key3=value3 * fragment: something * * More for URLs: RFC 2396 * * Note that all encoding is done using UTF-8! * * @see <a href="http://www.ietf.org/rfc/rfc2396.txt">RFC 2396</a> * @author flo */ public final class SuperURL { public enum Part { Scheme, UserInfo, Host, Port, Path, Query, Fragment; public static final Set<Part> ALL_PARTS = Collections.unmodifiableSet( EnumSet.allOf( Part.class ) ), NO_PARTS = Collections.unmodifiableSet( EnumSet.noneOf( Part.class ) ), UPTO_PATH = Collections.unmodifiableSet( EnumSet.of( Part.Scheme, Part.UserInfo, Part.Host, Part.Port ) ), FROM_PATH = Collections.unmodifiableSet( EnumSet.of( Part.Path, Part.Query, Part.Fragment ) ) ; } public enum Encode { Plain, Minimal, Full; } public enum FinalEncoding { Plain, Html, Attribute; } private static final EnumMap<Encode, SuperURLPrinter> printers; static { printers = new EnumMap<>( Encode.class ); printers.put( Encode.Plain, SuperURLPrinter.Plain ); printers.put( Encode.Minimal, SuperURLPrinter.MinimalEncoded ); printers.put( Encode.Full, SuperURLPrinter.FullEncoded ); } private EnumSet<Part> include = EnumSet.allOf( Part.class ); public static final String NOSCHEME = "NOSCHEME"; String scheme; UserInfo userInfo; Host host; Integer port; Path path; Query query; String fragment; public SuperURL(){} public SuperURL( String scheme, UserInfo userInfo, Host host, Integer port, Path path, Query query, String fragment ){ if( scheme != null ){ this.scheme = scheme; include.add( Part.Scheme ); } if( userInfo != null ){ this.userInfo = userInfo; include.add( Part.UserInfo ); } if( host != null ){ this.host = host; include.add( Part.Host ); } if( port != null ){ this.port = port; include.add( Part.Port ); } if( path != null ){ this.path = path; include.add( Part.Path ); } if( query != null ){ this.query = query; include.add( Part.Query ); } if( fragment != null ){ this.fragment = fragment; include.add( Part.Fragment ); } } /* --- Direct Access --- */ public SuperURL setInclude( Part part, boolean include ){ if( include ){ this.include.add( part ); } else { this.include.remove( part ); } return this; } public boolean isInclude( Part part ){ return include.contains( part ); } public String getScheme() { return scheme; } public SuperURL setScheme( String scheme ) { this.scheme = scheme; return this; } public SuperURL setIncludeScheme( boolean include ){ return setInclude( Part.Scheme, include ); } public boolean isIncludeScheme(){ return isInclude( Part.Scheme ); } public UserInfo getUserInfo() { return userInfo; } public SuperURL setUserInfo( UserInfo userInfo ) { this.userInfo = userInfo; return this; } public SuperURL setIncludeUserInfo( boolean include ){ return setInclude( Part.UserInfo, include ); } public boolean isIncludeUserInfo(){ return isInclude( Part.UserInfo ); } public Host getHost() { return host; } public SuperURL setHost( Host host ) { this.host = host; return this; } public SuperURL setIncludeHost( boolean include ){ return setInclude( Part.Host, include ); } public boolean isIncludeHost(){ return isInclude( Part.Host ); } public Integer getPort() { return port; } public SuperURL setPort( Integer port ) { this.port = port; return this; } public SuperURL setIncludePort( boolean include ){ return setInclude( Part.Port, include ); } public boolean isIncludePort(){ return isInclude( Part.Port ); } public String getFragment() { return fragment; } public SuperURL setFragment( String fragment ) { this.fragment = fragment; return this; } public SuperURL setIncludeFragment( boolean include ){ return setInclude( Part.Fragment, include ); } public boolean isIncludeFragment(){ return isInclude( Part.Fragment ); } // TODO: Da ist das automatische erzeugen des Pfades dazu gekommen. // Das können jetzt also beim Aufrufer Checks entfernt werden. public Path getPath() { if( path == null ) path = new Path(); return path; } public SuperURL setPath( Path path ) { this.path = path; return this; } public SuperURL setIncludePath( boolean include ){ return setInclude( Part.Path, include ); } public boolean isIncludePath(){ return isInclude( Part.Path ); } public Query getQuery() { if( query == null ) query = new Query(); return query; } public SuperURL setQuery( Query query ) { this.query = query; return this; } public SuperURL setIncludeQuery( boolean include ){ return setInclude( Part.Query, include ); } public boolean isIncludeQuery(){ return isInclude( Part.Query ); } /* --- Helpers --- */ public SuperURL setQueryParameter( String key, String value ){ getQuery().setValue( key, value ); return this; } public String getQueryParameter( String key ){ return getQuery().getValue( key ); } public boolean hasHost(){ return getHost() != null; } public boolean hasPath(){ return getPath() != null && getPath().toString().length() > 0; } public SuperURL appendPath( String path ){ return appendPath( path, true ); } public SuperURL appendPath( String path, boolean decode ){ Path newPath = SuperURLBuilders.Path().parse( path, decode ).build(); getPath().append( newPath ); getPath().endsWithSlash = newPath.endsWithSlash; return this; } /* --- ToString --- */ public String toStringEncode( Encode encode ){ return printers.get( encode ).toString( this ); } @Deprecated @Override public String toString(){ return toDebug(); } public String toRedirect(){ return SuperURLPrinter.FullEncoded .toString( this ); } public String toHtml(){ return SuperURLPrinter.MinimalEncoded .finishFor( FinalEncoding.Html ).toString( this ); } public String toDebug() { return SuperURLPrinter.MinimalEncoded .toString( this ); } // Called from AjaxFunction_cart public String toAjax() { return SuperURLPrinter.MinimalEncoded .toString( this ); } public String toAttribute(){ return SuperURLPrinter.MinimalEncoded .finishFor( FinalEncoding.Attribute ).toString( this ); } public String toText() { return SuperURLPrinter.Plain.toString( this ); } public String toValue() { return SuperURLPrinter.MinimalEncoded.toString( this ); } public String toUnique() { return SuperURLPrinter.FullEncoded.toString( this ); } static final String decode( String value ){ if( value == null ) return null; try { return URLDecoder.decode( value, Charsets.utf8 ); } catch( UnsupportedEncodingException e ) { throw new IllegalArgumentException( "Undecodeable: '" + value + "'" ); } } public URL toURL() { String asString = SuperURLPrinter.FullEncoded.toString( this ); try { return new URL( asString ); } catch( MalformedURLException e ){ throw new IllegalStateException( "Cannot create URL for: " + asString, e ); } } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ( ( scheme == null ) ? 0 : scheme.hashCode() ); result = prime * result + ( ( userInfo == null ) ? 0 : userInfo.hashCode() ); result = prime * result + ( ( host == null ) ? 0 : host.hashCode() ); result = prime * result + ( ( port == null ) ? 0 : port.hashCode() ); result = prime * result + ( ( path == null ) ? 0 : path.hashCode() ); result = prime * result + ( ( query == null ) ? 0 : query.hashCode() ); result = prime * result + ( ( fragment == null ) ? 0 : fragment.hashCode() ); return result; } @Override public boolean equals( Object obj ){ if( this == obj ) return true; if( obj == null ) return false; if( !( obj instanceof SuperURL ) ) return false; SuperURL other = (SuperURL) obj; return equals( other, Part.ALL_PARTS ); } public boolean equals( SuperURL other, Set<Part> parts ) { if( parts.contains( Part.Scheme ) ){ if( scheme == null ) { if( other.scheme != null ) return false; } else if( !scheme.equals( other.scheme ) ) return false; } if( parts.contains( Part.UserInfo ) ){ if( userInfo == null ) { if( other.userInfo != null ) return false; } else if( !userInfo.equals( other.userInfo ) ) return false; } if( parts.contains( Part.Host ) ){ if( host == null ) { if( other.host != null ) return false; } else if( !host.equals( other.host ) ) return false; } if( parts.contains( Part.Port ) ){ if( port == null ) { if( other.port != null ) return false; } else if( !port.equals( other.port ) ) return false; } if( parts.contains( Part.Path ) ){ if( path == null ) { if( other.path != null ) return false; } else if( !path.equals( other.path ) ) return false; } if( parts.contains( Part.Query ) ){ if( query == null ) { if( other.query != null ) return false; } else if( !query.equals( other.query ) ) return false; } if( parts.contains( Part.Fragment ) ){ if( fragment == null ) { if( other.fragment != null ) return false; } else if( !fragment.equals( other.fragment ) ) return false; } return true; } public static final class Host implements Iterable<String>{ public List<String> parts; public Host(){ parts = new LinkedList<String>(); } public Host copy() { Host result = new Host(); result.parts.addAll( parts ); return result; } public List<String> getParts(){ return parts; } public Host setParts( String ... parts ){ this.parts = new LinkedList<>( Arrays.asList( parts ) ); return this; } public Host setParts( LinkedList<String> parts ){ this.parts = parts; return this; } public int getSize(){ return this.parts.size(); } public String getTld(){ if( parts != null && parts.size() > 0 ) return parts.get( parts.size()-1 ); else return null; } public String getHost(){ if( parts != null && parts.size() > 0 ) return parts.get( 0 ); else return null; } public List<String> getNet(){ if( parts != null && parts.size() > 2 ){ LinkedList<String> result = new LinkedList<String>(); for( int i = 1; i < parts.size()-1; i++ ){ result.addLast( parts.get( i ) ); } return result; } else { return null; } } public String getNetAsString(){ return Str.join( ".", getNet() ); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ( ( parts == null ) ? 0 : parts.hashCode() ); return result; } @Override public boolean equals( Object obj ) { if( this == obj ) return true; if( obj == null ) return false; if( !( obj instanceof Host ) ) return false; Host other = (Host) obj; if( parts == null ) { if( other.parts != null ) return false; } else if( !parts.equals( other.parts ) ) return false; return true; } @Override public String toString() { return SuperURLPrinter.Plain.toString( this ); } public String toStringEncode( Encode encode ){ return printers.get( encode ).toString( this ); } public List<String> toList() { return new ArrayList<>( parts ); } @Override public Iterator<String> iterator() { return parts.iterator(); } } public static final class Path implements Iterable<String> { LinkedList<String> path; boolean endsWithSlash; boolean startsWithSlash; public Path(){ this( new LinkedList<String>(), false ); } public Path( LinkedList<String> path, boolean endsWithSlash ){ this.path = path; this.endsWithSlash = endsWithSlash; } public Path copy() { Path result = new Path(); result.path.addAll( path ); result.endsWithSlash = endsWithSlash; result.startsWithSlash = startsWithSlash; return result; } public Path map( Function<String,String> mapper ){ Path result = new Path(); for( String part : path ){ result.addLast( mapper.apply( part ) ); } result.endsWithSlash = endsWithSlash; result.startsWithSlash = startsWithSlash; return result; } public int length(){ return path.size(); } public String get( int index ){ return path.get( index ); } public boolean isEndsWithSlash(){ return endsWithSlash; } public Path setEndsWithSlash( boolean isEndsWithSlashes ){ endsWithSlash = isEndsWithSlashes; return this; } public boolean isStartsWithSlash(){ return startsWithSlash; } public Path setStartsWithSlash( boolean isStartsWithSlashes ){ startsWithSlash = isStartsWithSlashes; return this; } public boolean isAbsolute(){ return isStartsWithSlash(); } public boolean isRelative(){ return !isAbsolute(); } public String getParent(){ if( path != null && path.size() > 1 ) return path.get( path.size()-2 ); return null; } public String getLast(){ if( path != null && path.size() > 0 ) return path.getLast(); return null; } public Path replaceLast( String part ){ if( path != null && path.size() > 0 ){ if( part == null || part.length() == 0 ){ path.removeLast(); } else { path.set( path.size()-1, part ); } } return this; } public Path addLast( String part ){ if( part == null || part.length() == 0 ) return this; if( path == null ) path = new LinkedList<String>(); path.addLast( part ); return this; } public Path addAll( Path path ){ for( String item : path ){ addLast( item ); } setEndsWithSlash( path.isEndsWithSlash() ); return this; } public Path addAll( String ... path ){ for( String item : path ){ addLast( item ); } return this; } public Path removeLast(){ if( path != null && path.size() > 0 ) path.removeLast(); return this; } public String getFirst(){ if( path != null && path.size() > 0 ) return path.getFirst(); return null; } public Path replaceFirst( String part ){ if( path != null && path.size() > 0 ){ if( part != null && part.length() > 0 ){ path.set( 0, part ); } else { path.removeFirst(); } } return this; } public Path addFirst( String part ){ if( part == null || part.length() == 0 ) return this; if( path == null ) path = new LinkedList<String>(); path.addFirst( part ); return this; } public Path removeFirst(){ if( path != null && path.size() > 0 ) path.removeFirst(); return this; } public Path removeFirst( int amount ){ for( ;amount>0; amount-- ){ removeFirst(); } return this; } public Path append( Path path ){ for( String part : path.path ){ addLast( part ); } endsWithSlash = path.isEndsWithSlash(); return this; } public String getExtension(){ String lastPart = getLast(); if( lastPart != null ){ int pos = lastPart.lastIndexOf( '.' ); if( pos >= 0 ){ return lastPart.substring( pos+1 ); } } return null; } public String getLastWithoutExtension(){ String lastPart = getLast(); if( lastPart != null ){ int pos = lastPart.lastIndexOf( '.' ); if( pos >= 0 ){ return lastPart.substring( 0, pos ); } } return null; } public String getMimeType(){ if( endsWithSlash ){ return "text/directory"; } String extension = getExtension(); if( extension != null ) { /* Images */ if( extension.equalsIgnoreCase( "jpg" ) || extension.equalsIgnoreCase( "jpeg" ) ) { return "image/jpeg"; } else if( extension.equalsIgnoreCase( "png" ) ) { return "image/png"; } else if( extension.equalsIgnoreCase( "gif" ) ) { return "image/gif"; } else if( extension.equalsIgnoreCase( "ico" ) ) { return "image/x-icon"; } else if( extension.equalsIgnoreCase( "svg" ) ) { return "image/svg+xml"; /* css/js */ } else if( extension.equalsIgnoreCase( "css" ) ) { return "text/css"; } else if( extension.equalsIgnoreCase( "js" ) ) { return "text/javascript"; /* html */ } else if( extension.equalsIgnoreCase( "htm" ) || extension.equalsIgnoreCase( "html" ) /* Not! application/xhtml+xml. Not good in any browser. */ || extension.equalsIgnoreCase( "xhtml" ) ) { return "text/html"; } else if( extension.equalsIgnoreCase( "txt" ) ) { return "text/plain"; } else { // Use Java. (Doesn't know xhtml for example) String last = getLast(); return URLConnection.getFileNameMap().getContentTypeFor( last ); } } return null; } public List<String> toList(){ return new ArrayList<>( path ); } public String [] toArray(){ return path.toArray( new String[ path.size() ] ); } /** * @return slice of path as new Path * * @param index to start the slice from. 0 means complete copy. positive integer remove * from the left. negative from the right; */ public Path slice( int index ){ LinkedList<String> newPath = new LinkedList<String>( path ); while( index > 0 && newPath.size() > 0 ){ newPath.removeFirst(); index--; } while( index < 0 && newPath.size() > 0 ){ newPath.removeLast(); index--; } return new Path( newPath, endsWithSlash ); } @Override public String toString(){ return SuperURLPrinter.Plain.toString( this ); } public String toStringEncode( Encode encode ){ return printers.get( encode ).toString( this ); } @Override public Iterator<String> iterator() { return path.iterator(); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ( endsWithSlash ? 1231 : 1237 ); result = prime * result + ( ( path == null ) ? 0 : path.hashCode() ); result = prime * result + ( startsWithSlash ? 1231 : 1237 ); return result; } @Override public boolean equals( Object obj ) { if( this == obj ) return true; if( obj == null ) return false; if( !( obj instanceof Path ) ) return false; Path other = (Path) obj; if( startsWithSlash != other.startsWithSlash ) return false; if( endsWithSlash != other.endsWithSlash ) return false; if( path == null ) { if( other.path != null ) return false; } else if( !path.equals( other.path ) ) return false; return true; } public Path clear() { path.clear(); startsWithSlash = false; endsWithSlash = false; return this; } } public static final class Query implements Iterable<Query.QueryPart> { HashMap<String,LinkedList<QueryPart>> forName = new HashMap<String,LinkedList<QueryPart>>(); LinkedList<QueryPart> path = new LinkedList<QueryPart>(); public Query(){} public static Query fromLabel( String key, Label value ){ return new Query( new QueryPart( key, value.label() ) ); } public static Query fromPart( String key, String value ){ return new Query( new QueryPart( key, value ) ); } public Query( QueryPart ... parts ){ addAll( parts ); } public Query( Collection<QueryPart> parts ){ addAll( parts ); } public Query copy(){ Query result = new Query(); for( QueryPart part : path ){ result.add( part.copy() ); } return result; } public LinkedList<QueryPart> getPath(){ return path; } public void setPath( LinkedList<QueryPart> path ){ this.path = path; } public int size(){ return path.size(); } public boolean has( String key ){ return forName.containsKey( key ); } public LinkedList<QueryPart> getParts( String key ){ return forName.get( key ); } public QueryPart getPart( String key ){ if( forName.containsKey( key ) ){ LinkedList<QueryPart> parts = forName.get( key ); if( parts.size() > 0 ){ return parts.getLast(); } } return null; } public String getValue( String key ){ QueryPart part = getPart( key ); if( part != null ) return part.getValue(); else return null; } public List<String> getValues( String key ){ LinkedList<QueryPart> parts = getParts( key ); if( parts != null ){ LinkedList<String> result = new LinkedList<String>(); for( QueryPart part : parts ){ result.addLast( part.getValue() ); } return result; } else { return null; } } public Query addValue( String key, Object value ){ if( ! forName.containsKey( key ) ){ forName.put( key, new LinkedList<QueryPart>() ); } String valueAsString = null; if( value != null ){ if( value instanceof Label ){ valueAsString = ((Label)value).label(); } else { valueAsString = value.toString(); } } QueryPart part = new QueryPart( key, valueAsString ); forName.get( key ).addLast( part ); path.add( part ); return this; } public Query addAll( Map<String,Object> values ){ if( values != null ) for( Map.Entry<String,Object> entry : values.entrySet() ){ addValue( entry.getKey(), entry.getValue().toString() ); } return this; } public Query addAll( QueryPart ... parts ){ for( QueryPart part : parts ){ add( part ); } return this; } public Query addAll( Collection<QueryPart> parts ){ for( QueryPart part : parts ){ add( part ); } return this; } public Query setAll( Map<String,Object> values ){ if( values != null ) for( Map.Entry<String,Object> entry : values.entrySet() ){ setValue( entry.getKey(), entry.getValue().toString() ); } return this; } public Query addAll( Query other ){ for( QueryPart part : other.getPath() ){ add( part ); } return this; } public Query add( QueryPart part ){ addValue( part.getKey(), part.getValue() ); return this; } public Query setValue( String key, Object value ){ remValue( key ); addValue( key, value ); return this; } public Query remValue( String key ){ if( forName.containsKey( key ) ){ LinkedList<QueryPart> parts = forName.get( key ); for( QueryPart part : parts ){ path.remove( part ); } forName.remove( key ); } return this; } @Override public String toString(){ return SuperURLPrinter.Plain.toString( this ); } public String toStringEncode( Encode encode ){ return printers.get( encode ).toString( this ); } // Compatibility public Map<String,String[]> toParameterMap() { Map<String,String[]> result = new HashMap<>( forName.size() ); for( Map.Entry<String,LinkedList<QueryPart>> entry : forName.entrySet() ){ List<QueryPart> valueList = entry.getValue(); String [] values = new String[ valueList.size() ]; int i=0; for( QueryPart value : valueList ){ values[ i ] = value.getValue(); } result.put( entry.getKey(), values ); } return result; } /* * Built to match request.getParameterNames */ public Enumeration<String> getParameterNames(){ Vector<String> result = new Vector<>(); result.addAll( forName.keySet() ); return result.elements(); } public String [] getParameterValues( String key ){ List<QueryPart> parts = forName.get( key ); ArrayList<String> result = new ArrayList<>( parts.size() ); for( QueryPart part : parts ){ result.add( part.getValue() ); } return result.toArray( new String[ result.size() ] ); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ( ( path == null ) ? 0 : path.hashCode() ); return result; } @Override public boolean equals( Object obj ) { if( this == obj ) return true; if( obj == null ) return false; if( !( obj instanceof Query ) ) return false; Query other = (Query) obj; if( path == null ) { if( other.path != null ) return false; } else if( !path.equals( other.path ) ) return false; return true; } @Override public Iterator<QueryPart> iterator() { // This one is unmodifyable because modifying only the path breaks things return Collections.unmodifiableCollection( path ).iterator(); } public static final class QueryPart { String key; String value; public QueryPart( String key, String value ){ this.key = key; this.value = value; } public static QueryPart parse( String parseMe, boolean decode ){ String[] parts = Str.splitFastLimited( parseMe, '=', 2 ); String key = parts[ 0 ]; String value = null; if( parts.length > 1 ){ value = parts[ 1 ]; } if( decode ){ key = decode( key ); value = decode( value ); } return new QueryPart( key, value ); } public QueryPart copy(){ return new QueryPart( key, value ); } public String getKey() { return key; } public void setKey( String key ) { this.key = key; } public String getValue() { return value; } public void setValue( String val ) { this.value = val; } @Override public String toString(){ return key + "=" + value; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ( ( key == null ) ? 0 : key.hashCode() ); result = prime * result + ( ( value == null ) ? 0 : value.hashCode() ); return result; } @Override public boolean equals( Object obj ) { if( this == obj ) return true; if( obj == null ) return false; if( !( obj instanceof QueryPart ) ) return false; QueryPart other = (QueryPart) obj; if( key == null ) { if( other.key != null ) return false; } else if( !key.equals( other.key ) ) return false; if( value == null ) { if( other.value != null ) return false; } else if( !value.equals( other.value ) ) return false; return true; } } } public static final class UserInfo { String user; String pass; public UserInfo(){} public UserInfo( String user, String pass ){ this.user = user; this.pass = pass; } public static UserInfo parse( String parseMe ){ return parse( parseMe, true ); } public static UserInfo parse( String parseMe, boolean decode ){ String[] parts = Str.splitFastLimited( parseMe, ':', 2 ); String user = parts[ 0 ]; String pass = null; if( parts.length > 1 ) pass = parts[ 1 ]; if( decode ){ user = decode( user ); pass = decode( pass ); } return new UserInfo( user, pass ); } public UserInfo copy() { return new UserInfo( user, pass ); } public String getUser() { return user; } public UserInfo setUser( String user ) { this.user = user; return this; } public String getPass() { return pass; } public UserInfo setPass( String pass ) { this.pass = pass; return this; } @Override public String toString(){ return SuperURLPrinter.Plain.toString( this ); } public String toStringEncode( Encode encode ){ return printers.get( encode ).toString( this ); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ( ( pass == null ) ? 0 : pass.hashCode() ); result = prime * result + ( ( user == null ) ? 0 : user.hashCode() ); return result; } @Override public boolean equals( Object obj ) { if( this == obj ) return true; if( obj == null ) return false; if( !( obj instanceof UserInfo ) ) return false; UserInfo other = (UserInfo) obj; if( pass == null ) { if( other.pass != null ) return false; } else if( !pass.equals( other.pass ) ) return false; if( user == null ) { if( other.user != null ) return false; } else if( !user.equals( other.user ) ) return false; return true; } } /* *************************************************** * BUILDER ************************************************** */ private static final SuperURLBuilder_String BUILDER_STRING = SuperURLBuilders.fromString(); private static final SuperURLBuilder_Request BUILDER_REQUEST = SuperURLBuilders.fromRequest(); private static final SuperURLBuilder_URI BUILDER_URI = SuperURLBuilders.fromURI(); private static final SuperURLBuilder_Copy BUILDER_SHALLOW_COPY = SuperURLBuilders.fromSuperURL().usingDeepCopy( false ); private static final SuperURLBuilder_Copy BUILDER_DEEP_COPY = SuperURLBuilders.fromSuperURL().usingDeepCopy( true ); public static SuperURL fromString( String parseMe ){ return BUILDER_STRING.build( parseMe ); } public static SuperURL fromRequest( HttpServletRequest request ){ return BUILDER_REQUEST.build( request ); } public static SuperURL fromURI( URI uri ){ return BUILDER_URI.build( uri ); } public static SuperURL copyShallow( SuperURL other ){ return BUILDER_SHALLOW_COPY.build( other ); } public static SuperURL copyDeep( SuperURL other ){ return BUILDER_DEEP_COPY.build( other ); } }