diff --git a/Jenkinsfile b/Jenkinsfile deleted file mode 100644 index 5425c3c..0000000 --- a/Jenkinsfile +++ /dev/null @@ -1,83 +0,0 @@ -pipeline { - - agent none - - stages { - - stage('Compile') { - agent { - label 'p4java' - } - steps { - sh './gradlew clean assemble' - - } - } - - stage('Verification') { - parallel { - - stage('ubuntu') { - agent { - label 'p4java' - } - stages { - stage('Test') { - steps { - lock("eng-p4java-vm_lock") { - sh './gradlew clean build' - - } - } - } - } - post { - always { - report('UbuntuTestReport') - - } - } - } - - stage('win') { - agent { - label 'p4java-win' - } - stages { - stage('Test') { - steps { - lock("eng-p4java-vm_lock") { - bat label: '', script: 'gradlew clean build' - - } - } - } - } - post { - always { - report('WindowsTestReport') - } - } - } - } - } - - stage('Launch system tests') { - steps { - build job: '/p4java-system-tests/main', wait: false - } - } - } -} - - -void report(String name) { - publishHTML target: [ - allowMissing : false, - alwaysLinkToLastBuild: true, - keepAll : true, - reportDir : 'build/reports/tests/test/', - reportFiles : 'index.html', - reportName : name - ] -} diff --git a/LICENSE.txt b/LICENSE.txt index 848d3a5..817b108 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,7 +1,6 @@ Copyright (c) 2023, Perforce Software, Inc. All rights reserved. - BY INSTALLING OR DOWNLOADING THE SOFTWARE, YOU ARE ACCEPTING AND AGREEING TO THE TERMS OF THIS END USER LICENSE AGREEMENT (THE "LICENSE AGREEMENT"), AND IT LEGALLY BINDS YOU AND YOUR EMPLOYER (COLLECTIVELY THE "LICENSEE"), AND PERFORCE SOFTWARE, INC., A DELAWARE @@ -10,15 +9,11 @@ ANY TERMS, CONDITIONS, AND RESTRICTIONS CONTAINED IN ANY ORDER RELATING TO THE S IF THE LICENSEE DOES NOT ACCEPT AND AGREE TO THE TERMS AND CONDITIONS OF THE LICENSE AGREEMENT THEN DO NOT DOWNLOAD, INSTALL, OR OTHERWISE USE THE SOFTWARE. - - THE RIGHT TO USE THE SOFTWARE IS CONDITIONAL UPON ACCEPTANCE OF THE LICENSE AGREEMENT, UNLESS THE LICENSEE HAS ENTERED INTO A WRITTEN AND DULY SIGNED LICENSE AGREEMENT WITH PERFORCE, IN WHICH CASE SUCH SIGNED LICENSE AGREEMENT WILL GOVERN THE LICENSEE'S USE OF THE SOFTWARE. - - Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -51,7 +46,7 @@ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABI TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - This distribution also includes the following third party software; please consult the + This distribution also includes the following third party software; please consult the accompanying license file for the license terms that apply to that software only: commons-codec-1.15.jar @@ -119,6 +114,7 @@ jzlib-1.1.3.jar Reference: http://www.jcraft.com/jzlib/LICENSE.txt Author: ymnk + Appendix: ----- BSD 3 diff --git a/RELEASE.md b/RELEASE.md index e385342..7dd7b19 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -1,7 +1,7 @@ Release Notes for P4Java, the Perforce Java API - Version 2022.1 + Version 2022.2 Introduction @@ -120,6 +120,29 @@ Known Limitations /lib/security/local_policy.jar /lib/security/US_export_policy.jar + +------------------------------------------- +Updates in 2022.2 (2022.2/2444480) (2023/05/23) + + #2435337 (Job #114238) + Enhanced functionality of p4 print command with the addition of + --offset and --size options. + + #2426294 (Job #114313) + Added support for 'MaxMemory' in the group spec. + + #2439567, #2443196 (Job #114313) + Added support for 'Components' in the stream spec. + +------------------------------------------- +Updates in 2022.1 Patch 2 (2023/03/27) + + #2421117 (Job #112706) + Fixed a bug where P4Java failed to update the sync time in db.have + during a sync operation + + #2409816 (Job #113999) + Fixed a bug in decoding UTF-16 encoded files, which leads to file corruption ------------------------------------------- Updates in 2022.1 Patch 1 (2023/01/12) @@ -140,26 +163,26 @@ Updates in 2022.1 (2022/09/29) Added support to uncompress the compressed files received from server #2325394 (Job #111560) - Added functionality to remove file revision attributes + Added functionality to remove file revision attributes #2322763 (Job #110629) - Added support for special characters (@, %, # and *) in the file spec - builder by adding ‘makeFileSpecListSpecialChars’ method + Added support for special characters (@, %, # and *) in the file spec + builder by adding ‘makeFileSpecListSpecialChars’ method #2328997, #2330279 (Job #110207) - Fixed a bug where sync of unicode files with bad charset was causing - corrupt files on the workspace without throwing an error + Fixed a bug where sync of unicode files with bad charset was causing + corrupt files on the workspace without throwing an error #2346483 (Job #109218) - Fixed a bug where moves with an overlap between source and target - file/directory names raised an error. + Fixed a bug where moves with an overlap between source and target + file/directory names raised an error. #2333818 (Job #112005) - Fixed a bug where unending sync operation was being caused when the - client compression option was set + Fixed a bug where unending sync operation was being caused when the + client compression option was set - With this release we have migrated P4Java build from from Maven to - Gradle using Gradle wrapper 7.5 + With this release we have migrated P4Java build from from Maven to + Gradle using Gradle wrapper 7.5 ------------------------------------------- Updates in 2021.2 Patch 5 diff --git a/build.gradle b/build.gradle index fa4e22c..bdbe3b9 100644 --- a/build.gradle +++ b/build.gradle @@ -12,7 +12,7 @@ targetCompatibility = 1.8 repositories { maven { url "https://repo.maven.apache.org/maven2" } - maven { url "https://artifactory.bnr.perforce.com:8443/artifactory/snapshots/" } + maven { url "https://artifactory.bnr.perforce.com/artifactory/snapshots/" } } dependencies { @@ -36,6 +36,14 @@ jar { from { generatePomFileForMavenJavaPublication } rename ".*", "pom.xml" } + manifest { + attributes["Name"] = "com/perforce/p4java/" + attributes["Main-Class"] = "com.perforce.p4java.Metadata" + attributes["Implementation-Title"] = "p4java" + attributes["Implementation-Version"] = archiveVersion + attributes["Implementation-Vendor-Id"] = "com.perforce" + attributes["Implementation-Vendor"] = "Perforce Software" + } } publishing { @@ -94,7 +102,7 @@ publishing { } maven { name = 'artifactory-snapshots' - url = 'https://artifactory.bnr.perforce.com:8443/artifactory/snapshots/' + url = 'https://artifactory.bnr.perforce.com/artifactory/snapshots/' credentials { username = mavenUser password = mavenPassword @@ -102,7 +110,7 @@ publishing { } maven { name = 'artifactory-release' - url = 'https://artifactory.bnr.perforce.com:8443/artifactory/releases/' + url = 'https://artifactory.bnr.perforce.com/artifactory/releases/' credentials { username = mavenUser password = mavenPassword diff --git a/gradlew.bat b/gradlew.bat index b113f38..f955316 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -1,84 +1,84 @@ -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto init - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/src/main/java/com/perforce/p4java/core/IStream.java b/src/main/java/com/perforce/p4java/core/IStream.java index 914fd98..9234eeb 100644 --- a/src/main/java/com/perforce/p4java/core/IStream.java +++ b/src/main/java/com/perforce/p4java/core/IStream.java @@ -24,13 +24,17 @@ public interface IStream extends IStreamSummary { * Defines an "extraTag*" field */ public interface IExtraTag { + String getName(); + void setName(String name); - + String getType(); + void setType(String type); - + String getValue(); + void setValue(String value); }; @@ -38,7 +42,7 @@ public interface IExtraTag { * Return the view map associated with this stream. One or more mappings * that define file paths in the stream view. Each line is of the form: * path_type view_path [depot_path] - * + * * @return non-null list of IStreamViewMapping mappings for this stream. */ ViewMap getStreamView(); @@ -47,9 +51,8 @@ public interface IExtraTag { * Set the view map associated with this stream spec. This will not * change the associated stream spec on the Perforce server unless you * arrange for the update to server. - * - * @param streamView - * new view mappings for the stream. + * + * @param streamView new view mappings for the stream. */ void setStreamView(ViewMap streamView); @@ -58,9 +61,9 @@ public interface IExtraTag { * Return the remapped view map associated with this stream. Optional; one * or more mappings that define how stream view paths are to be remapped in * client views. Each line is of the form: view_path_1 view_path_2 - * + * * @return possibly-null (optional) list of IStreamRemappedMapping mappings - * for this stream. + * for this stream. */ ViewMap getRemappedView(); @@ -68,9 +71,8 @@ public interface IExtraTag { * Set the remapped view map associated with this stream spec. This will * not change the associated stream spec on the Perforce server unless you * arrange for the update to server. - * - * @param remappedView - * new remapped view mappings for the stream. + * + * @param remappedView new remapped view mappings for the stream. */ void setRemappedView(ViewMap remappedView); @@ -79,9 +81,9 @@ public interface IExtraTag { * of file or directory names to be ignored in client views. mappings in the * "Ignored" field may appear in any order. Ignored names are inherited by * child stream client views. - * + * * @return possibly-null (optional) list of IStreamIgnoredMapping mappings - * to be ignored for this stream. + * to be ignored for this stream. */ ViewMap getIgnoredView(); @@ -96,9 +98,9 @@ public interface IExtraTag { /** * Return the automatically generated client view map associated with this * stream. Maps files in the depot to files in your client workspace. - * + * * @return possibly-null list of automatically generated IClientViewMapping - * mappings associated with this stream. + * mappings associated with this stream. */ ViewMap getClientView(); @@ -112,11 +114,11 @@ public interface IExtraTag { /** * Return a list of extra tags associated with this stream. - * + * * @return possibly-null list of extra tags associated with this stream. */ List getExtraTags(); - + /** * Set the extra tags associated with this stream. This will not change * the associated stream spec on the Perforce server unless you arrange for @@ -124,10 +126,17 @@ public interface IExtraTag { * @param extraTags extra tags */ void setExtraTags(List extraTags); - + /** * Set the server to type of IOptionsServer, overriding the default IServer. * @param server server */ void setServer(IOptionsServer server); + + ViewMap getComponents(); + + /** + * @param components A stream Components + */ + void setComponents(ViewMap components); } diff --git a/src/main/java/com/perforce/p4java/core/IStreamComponentMapping.java b/src/main/java/com/perforce/p4java/core/IStreamComponentMapping.java new file mode 100644 index 0000000..36b8935 --- /dev/null +++ b/src/main/java/com/perforce/p4java/core/IStreamComponentMapping.java @@ -0,0 +1,41 @@ +package com.perforce.p4java.core; + +import java.util.Arrays; +import java.util.Optional; + +public interface IStreamComponentMapping extends IMapEntry { + + enum Type { + READONLY("readonly"), WRITE_IMPORT("writeimport+"), WRITE_ALL("writeall"); + + final String text; + + Type(String text) { + this.text = text; + } + + public String getText() { + return text; + } + + public static Type fromString(String typeString) { + return Arrays.stream(Type.values()) + .filter(type -> typeString.equals(type.getText())) + .findAny() + .orElse(null); + } + } + + Type getComponentType(); + + void setComponentType(Type type); + + String getDirectory(); + + void setDirectory(String directory); + + String getStream(); + + void setStream(String stream); + +} diff --git a/src/main/java/com/perforce/p4java/core/IUserGroup.java b/src/main/java/com/perforce/p4java/core/IUserGroup.java index 83f4325..001eff3 100644 --- a/src/main/java/com/perforce/p4java/core/IUserGroup.java +++ b/src/main/java/com/perforce/p4java/core/IUserGroup.java @@ -213,4 +213,20 @@ public interface IUserGroup extends IServerResource { * @param users users */ void setUsers(List users); + + /** + * Get Maximum amount of megabytes of random-access memory that a command can use when + * run by any member of the group. + * + * @return amount of memory in MB + */ + int getMaxMemory(); + + /** + * Set Maximum amount of megabytes of random-access memory that a command can use when + * run by any member of the group. + * + * @param maxMemory - in MB + */ + void setMaxMemory(int maxMemory); } diff --git a/src/main/java/com/perforce/p4java/core/file/IFileSpec.java b/src/main/java/com/perforce/p4java/core/file/IFileSpec.java index 705444b..1c186fc 100644 --- a/src/main/java/com/perforce/p4java/core/file/IFileSpec.java +++ b/src/main/java/com/perforce/p4java/core/file/IFileSpec.java @@ -826,4 +826,8 @@ public interface IFileSpec extends IFileOperationResult { List getResolveTypes(); void setResolveTypes(List types); + + long getSyncTime(); + + void setSyncTime(long syncTime); } diff --git a/src/main/java/com/perforce/p4java/impl/generic/core/InputMapper.java b/src/main/java/com/perforce/p4java/impl/generic/core/InputMapper.java index 212c5ba..8153bf7 100644 --- a/src/main/java/com/perforce/p4java/impl/generic/core/InputMapper.java +++ b/src/main/java/com/perforce/p4java/impl/generic/core/InputMapper.java @@ -24,11 +24,13 @@ import com.perforce.p4java.core.IMapEntry; import com.perforce.p4java.core.IReviewSubscription; import com.perforce.p4java.core.IStream; +import com.perforce.p4java.core.IStreamComponentMapping; import com.perforce.p4java.core.IStreamSummary; import com.perforce.p4java.core.IUser; import com.perforce.p4java.core.IUserGroup; import com.perforce.p4java.core.ViewMap; import com.perforce.p4java.core.file.IFileSpec; +import com.perforce.p4java.exception.ConnectionException; import com.perforce.p4java.exception.P4JavaException; import com.perforce.p4java.impl.generic.client.ClientView; import com.perforce.p4java.impl.mapbased.MapKeys; @@ -331,11 +333,12 @@ public static Map map(IUser user) { /** * Map a P4Java IUserGroup object to an IServer input map. * - * @param group candidate user group object + * @param group candidate user group object + * @param server version of p4d server * @return non-null map suitable for use with execMapCmd */ - public static Map map(IUserGroup group) { - Map groupMap = new HashMap(); + public static Map map(IUserGroup group, IServer server) throws ConnectionException { + Map groupMap = new HashMap<>(); if (group != null) { if (group.getName() != null) groupMap.put(MapKeys.GROUP_KEY, group.getName()); @@ -346,6 +349,9 @@ public static Map map(IUserGroup group) { if (group.getMaxOpenFiles() != -Integer.MAX_VALUE) { groupMap.put(MapKeys.MAXOPENFILES_KEY, getUGValue(group.getMaxOpenFiles())); } + if (server.getServerVersion() >= 20222) { + groupMap.put(MapKeys.MAX_MEMORY_KEY, getUGValue(group.getMaxMemory())); + } groupMap.put(MapKeys.PASSWORD_TIMEOUT_KEY, getUGValue(group.getPasswordTimeout())); if (group.getSubgroups() != null) { int i = 0; @@ -506,6 +512,15 @@ public static Map map(IStream stream, IServer server) throws P4J streamMap.putAll(parseMap(stream.getRemappedView(), MapKeys.REMAPPED_KEY)); streamMap.putAll(parseMap(stream.getIgnoredView(), MapKeys.IGNORED_KEY)); + if (stream.getComponents() != null && server.getServerVersion() >= 20221) { + StringBuffer buffer = new StringBuffer(); + for (IStreamComponentMapping mapping : stream.getComponents().getEntryList()) { + buffer.append(mapping.toString()); + buffer.append("\n"); + } + streamMap.put(MapKeys.COMPONENTS_KEY, buffer.toString().trim()); + } + // For pre 21.1 servers without ParentView. IStreamSummary.ParentView parentView; diff --git a/src/main/java/com/perforce/p4java/impl/generic/core/Stream.java b/src/main/java/com/perforce/p4java/impl/generic/core/Stream.java index 78d1124..1b898e7 100644 --- a/src/main/java/com/perforce/p4java/impl/generic/core/Stream.java +++ b/src/main/java/com/perforce/p4java/impl/generic/core/Stream.java @@ -6,6 +6,7 @@ import com.perforce.p4java.Log; import com.perforce.p4java.client.IClientViewMapping; import com.perforce.p4java.core.IStream; +import com.perforce.p4java.core.IStreamComponentMapping; import com.perforce.p4java.core.IStreamIgnoredMapping; import com.perforce.p4java.core.IStreamRemappedMapping; import com.perforce.p4java.core.IStreamSummary; @@ -29,6 +30,8 @@ import java.util.Locale; import java.util.Map; +import static com.perforce.p4java.impl.mapbased.MapKeys.SPACE; + /** * Simple default implementation class for the IStream interface. */ @@ -39,6 +42,8 @@ public class Stream extends StreamSummary implements IStream { protected ViewMap remappedView = null; protected ViewMap ignoredView = null; protected ViewMap clientView = null; + + protected ViewMap components = null; protected List extraTags = null; /** @@ -149,9 +154,9 @@ public static Stream newStream(IOptionsServer server, String streamPath, String description, String options, String[] viewPaths, String[] remappedPaths, String[] ignoredPaths) { - return newStream(server, streamPath, type, parentStreamPath, name, + return getStream(server, streamPath, type, parentStreamPath, name, description, options, viewPaths, remappedPaths, ignoredPaths, - null); + null, null); } /** @@ -195,6 +200,61 @@ public static Stream newStream(IOptionsServer server, String streamPath, String description, String options, String[] viewPaths, String[] remappedPaths, String[] ignoredPaths, String[] clientViewPaths) { + return getStream(server, streamPath, type, parentStreamPath, name, description, options, viewPaths, remappedPaths, + ignoredPaths, clientViewPaths, null); + } + + /** + * Simple factory / convenience method for creating a new local Stream + * object with defult values. + * + * @param server non-null server to be associated with the new stream spec. + * @param streamPath non-null stream's path in a stream depot, of the form + * //depotname/streamname. + * @param type non-null stream type of 'mainline', 'development', or + * 'release'. + * @param parentStreamPath parent of this stream. Can be null if the stream type is + * 'mainline', otherwise must be set to an existing stream. + * @param name an alternate name of the stream, for use in display outputs. + * Defaults to the 'streamname' portion of the stream path. Can + * be changed. + * @param description if not null, used as the new stream spec's description field; + * if null, uses the Stream.DEFAULT_DESCRIPTION field. + * @param options flags to configure stream behavior: allsubmit/ownersubmit + * [un]locked [no]toparent [no]fromparent mergedown/mergeany. + * @param viewPaths one or more lines that define file paths in the stream view. + * Each line is of the form: path_type view_path depot_path + * @param remappedPaths optional; one or more lines that define how stream view paths + * are to be remapped in client views. Each line is of the form: + * view_path_1 view_path_2 + * @param ignoredPaths optional; a list of file or directory names to be ignored in + * client views. For example: + * /tmp # ignores files named 'tmp' + * /tmp/... # ignores dirs named 'tmp' + * .tmp # ignores file names ending in '.tmp' + * @param clientViewPaths automatically generated; maps files in the depot to files in + * your client workspace. For example: + * //p4java_stream/dev/... ... + * //p4java_stream/dev/readonly/sync/p4cmd/%%1 readonly/sync/p4cmd/%%1 + * -//p4java_stream/.../temp/... .../temp/... + * -//p4java_stream/....class ....class + * @param components Add stream components. + * Syntax: component_type component_folder component_stream[@[change | automatic_label]] + * eg: readonly dirB //streams/B + * @return new local Stream object. + */ + public static Stream newStream(IOptionsServer server, String streamPath, + String type, String parentStreamPath, String name, + String description, String options, String[] viewPaths, + String[] remappedPaths, String[] ignoredPaths, + String[] clientViewPaths, String[] components) { + return getStream(server, streamPath, type, parentStreamPath, name, description, options, viewPaths, remappedPaths, + ignoredPaths, clientViewPaths, components); + } + + private static Stream getStream(IOptionsServer server, String streamPath, String type, String parentStreamPath, + String name, String description, String options, String[] viewPaths, String[] remappedPaths, + String[] ignoredPaths, String[] clientViewPaths, String[] components) { if (server == null) { throw new NullPointerError("null server in Stream.newStream()"); } @@ -258,6 +318,19 @@ public static Stream newStream(IOptionsServer server, String streamPath, } } + ViewMap streamComponents = null; + if (components != null) { + streamComponents = new ViewMap<>(); + int i = 0; + for (String mapping : components) { + if (mapping == null) { + throw new NullPointerError("null client view mapping string passed to Stream.newStream"); + } + streamComponents.addEntry(new StreamComponentMapping(i, mapping)); + i++; + } + } + IOptions streamOptions = new Options(); if (options != null) { streamOptions = new Options(options); @@ -286,7 +359,9 @@ public static Stream newStream(IOptionsServer server, String streamPath, streamOptions, streamView, remappedView, - ignoredView); + ignoredView, + null, + streamComponents); } /** @@ -505,6 +580,62 @@ public void setIgnorePath(String ignorePath) { } } + public static class StreamComponentMapping extends MapEntry implements IStreamComponentMapping { + Type type = null; + + public StreamComponentMapping(int order, Type type, String folder, String stream) { + super(order, folder, stream); + this.type = type; + } + + public StreamComponentMapping(int order, String rawComponentString) { + String[] s = rawComponentString.split(SPACE); + if (s.length == 3) { + this.order = order; + this.type = Type.fromString(s[0]); + this.left = s[1]; + this.right = s[2]; + } else { + Log.error("Bad stream components mapping. " + rawComponentString); + } + } + + @Override + public Type getComponentType() { + return type; + } + + @Override + public void setComponentType(Type type) { + this.type = type; + } + + @Override + public String getDirectory() { + return left; + } + + @Override + public void setDirectory(String directory) { + this.left = directory; + } + + @Override + public String getStream() { + return right; + } + + @Override + public void setStream(String stream) { + this.right = stream; + } + + @Override + public String toString() { + return type.getText() + SPACE + left + SPACE + right; + } + } + /** * Default constructor. All fields set to null or false. */ @@ -535,7 +666,7 @@ public Stream(String stream, Type type, String parent, Date accessed, ViewMap ignoredView) { this(stream, type, parent, accessed, updated, name, description, - ownerName, options, streamView, remappedView, ignoredView, null); + ownerName, options, streamView, remappedView, ignoredView, null, null); } /** @@ -555,12 +686,39 @@ public Stream(String stream, Type type, String parent, Date accessed, * @param ignoredView ignoredView * @param clientView clientView */ + public Stream(String stream, Type type, String parent, Date accessed, + Date updated, String name, String description, String ownerName, + IOptions options, ViewMap streamView, + ViewMap remappedView, + ViewMap ignoredView, ViewMap clientView) { + this(stream, type, parent, accessed, updated, name, description,ownerName, options, streamView, remappedView, + ignoredView, clientView, null); + } + + /** + * Construct a new Stream from explicit field values. + * + * @param stream stream + * @param type type + * @param parent parent + * @param accessed accessed + * @param updated updated + * @param name name + * @param description description + * @param ownerName ownerName + * @param options options + * @param streamView streamView + * @param remappedView remappedView + * @param ignoredView ignoredView + * @param clientView clientView + * @param components components + */ public Stream(String stream, Type type, String parent, Date accessed, Date updated, String name, String description, String ownerName, IOptions options, ViewMap streamView, ViewMap remappedView, ViewMap ignoredView, - ViewMap clientView) { + ViewMap clientView, ViewMap components) { setStream(stream); setType(type); setParent(parent); @@ -575,6 +733,7 @@ public Stream(String stream, Type type, String parent, Date accessed, this.remappedView = remappedView; this.ignoredView = ignoredView; this.clientView = clientView; + this.components = components; } /** @@ -593,6 +752,7 @@ public Stream(Map map, IServer server) { this.ignoredView = new ViewMap<>(); this.clientView = new ViewMap<>(); this.extraTags = new ArrayList<>(); + this.components = new ViewMap<>(); if (map != null) { String key = MapKeys.PATHS_KEY; @@ -735,6 +895,24 @@ public Stream(Map map, IServer server) { } } } + + key = MapKeys.COMPONENTS_KEY; + for (int i = 0; ; i++) { + if (!map.containsKey(key + i)) { + break; + } else if (map.get(key + i) != null) { + try { + String components = (String) map.get(key + i); + this.components.getEntryList().add(new StreamComponentMapping(i, components)); + + } catch (Throwable thr) { + Log.error("Unexpected exception in Stream map-based constructor: " + + thr.getLocalizedMessage()); + Log.exception(thr); + } + } + } + } } @@ -897,4 +1075,14 @@ public List getExtraTags() { public void setExtraTags(List extraTags) { this.extraTags = extraTags; } + + @Override + public ViewMap getComponents() { + return components; + } + + @Override + public void setComponents(ViewMap components) { + this.components = components; + } } diff --git a/src/main/java/com/perforce/p4java/impl/generic/core/UserGroup.java b/src/main/java/com/perforce/p4java/impl/generic/core/UserGroup.java index dac2c9e..a855d42 100644 --- a/src/main/java/com/perforce/p4java/impl/generic/core/UserGroup.java +++ b/src/main/java/com/perforce/p4java/impl/generic/core/UserGroup.java @@ -51,6 +51,8 @@ public class UserGroup extends ServerResource implements IUserGroup { private List users = new ArrayList<>(); private int maxOpenFiles = UNDEFINED; + private int maxMemory = UNSET; + /** * Simple convenience factory method to return a new local UserGroup object. @@ -115,6 +117,7 @@ public UserGroup(@Nullable final Map map) { if (map.containsKey(MapKeys.MAXOPENFILES_KEY)) { maxOpenFiles = parseGroupIntValue((String) map.get(MapKeys.MAXOPENFILES_KEY)); } + maxMemory = parseGroupIntValue((String) map.get(MapKeys.MAX_MEMORY_KEY)); timeout = parseGroupIntValue((String) map.get(MapKeys.TIMEOUT_KEY)); passwordTimeout = parseGroupIntValue( (String) map.get(MapKeys.PASSWORD_TIMEOUT_KEY)); @@ -237,6 +240,16 @@ public void setMaxOpenFiles(int maxOpenFiles) { this.maxOpenFiles = maxOpenFiles; } + @Override + public int getMaxMemory() { + return this.maxMemory; + } + + @Override + public void setMaxMemory(int maxMemory) { + this.maxMemory = maxMemory; + } + @Override public void refresh() throws ConnectionException, RequestException, AccessException { IServer refreshServer = server; diff --git a/src/main/java/com/perforce/p4java/impl/generic/core/file/FileSpec.java b/src/main/java/com/perforce/p4java/impl/generic/core/file/FileSpec.java index 78f77a0..33f4e80 100644 --- a/src/main/java/com/perforce/p4java/impl/generic/core/file/FileSpec.java +++ b/src/main/java/com/perforce/p4java/impl/generic/core/file/FileSpec.java @@ -36,6 +36,7 @@ import java.util.Date; import java.util.List; import java.util.Map; +import java.util.Objects; import static com.perforce.p4java.common.base.P4ResultMapUtils.parseInt; import static com.perforce.p4java.common.base.P4ResultMapUtils.parseLong; @@ -73,6 +74,7 @@ import static com.perforce.p4java.impl.mapbased.rpc.func.RpcFunctionMapKey.STARTFROMREV; import static com.perforce.p4java.impl.mapbased.rpc.func.RpcFunctionMapKey.START_TO_REV; import static com.perforce.p4java.impl.mapbased.rpc.func.RpcFunctionMapKey.STATUS; +import static com.perforce.p4java.impl.mapbased.rpc.func.RpcFunctionMapKey.SYNCTIME; import static com.perforce.p4java.impl.mapbased.rpc.func.RpcFunctionMapKey.TIME; import static com.perforce.p4java.impl.mapbased.rpc.func.RpcFunctionMapKey.TO_FILE; import static com.perforce.p4java.impl.mapbased.rpc.func.RpcFunctionMapKey.TREE; @@ -155,6 +157,8 @@ public class FileSpec extends ServerResource implements IFileSpec { protected IClient client = null; + private long syncTime; + /** * Default constructor. Sets all paths, labels, dates, etc. to null; * revisions to IFileSpec.NO_FILE_REVISION; client and server references to @@ -365,6 +369,10 @@ public FileSpec(@Nullable final Map map, @Nonnull final IServer if (map.containsKey(PATH + indexStr)) { setLocalPath(new FilePath(PathType.LOCAL, parseString(map, PATH + indexStr), true)); } + if (map.containsKey(SYNCTIME + indexStr)) { + long seconds = Long.parseLong((Objects.requireNonNull(parseString(map, SYNCTIME + indexStr)))); + setSyncTime(seconds * 1000); + } setFileType(parseString(map, TYPE + indexStr)); setAction(FileAction.fromString(parseString(map, ACTION + indexStr))); setUserName(parseString(map, USER + indexStr)); @@ -526,6 +534,14 @@ public int getChangelistId() { return changeListId; } + public long getSyncTime() { + return syncTime; + } + + public void setSyncTime(long syncTime) { + this.syncTime = syncTime; + } + @Override public String getClientName() { return clientName; diff --git a/src/main/java/com/perforce/p4java/impl/mapbased/MapKeys.java b/src/main/java/com/perforce/p4java/impl/mapbased/MapKeys.java index add3ad0..e1d7e49 100644 --- a/src/main/java/com/perforce/p4java/impl/mapbased/MapKeys.java +++ b/src/main/java/com/perforce/p4java/impl/mapbased/MapKeys.java @@ -26,6 +26,8 @@ public class MapKeys { public static final String DOUBLE_LF = "\n\n"; public static final String EMPTY = ""; + public static final String SPACE = " "; + // Order of definitions below is NOT significant... public static final String ACCESS_KEY = "Access"; public static final String ACCESSED_KEY = "Accessed"; @@ -78,6 +80,8 @@ public class MapKeys { public static final String MAXSCANROWS_KEY = "MaxScanRows"; public static final String MAXSCANROWS_LC_KEY = "maxScanRows"; public static final String MAXOPENFILES_KEY = "MaxOpenFiles"; + + public static final String MAX_MEMORY_KEY = "MaxMemory"; public static final String NAME_KEY = "Name"; public static final String NAME_LC_KEY = "name"; public static final String OPTIONS_KEY = "Options"; @@ -91,6 +95,7 @@ public class MapKeys { public static final String PASSWORD_TIMEOUT_KEY = "PasswordTimeout"; public static final String PASSWORD_TIMEOUT_LC_KEY = "passTimeout"; // especially egregious, this one... public static final String PATHS_KEY = "Paths"; + public static final String COMPONENTS_KEY = "Components"; public static final String PROG_LC_KEY = "prog"; public static final String PROTECTIONS_KEY = "Protections"; public static final String REMAPPED_KEY = "Remapped"; diff --git a/src/main/java/com/perforce/p4java/impl/mapbased/rpc/RpcServer.java b/src/main/java/com/perforce/p4java/impl/mapbased/rpc/RpcServer.java index 2a2e77e..03925b1 100644 --- a/src/main/java/com/perforce/p4java/impl/mapbased/rpc/RpcServer.java +++ b/src/main/java/com/perforce/p4java/impl/mapbased/rpc/RpcServer.java @@ -773,6 +773,9 @@ public String getAuthTicket(final String userName) { @Override public String getAuthTicket(final String userName, final String serverId) { + if (isBlank(userName) || authTickets.isEmpty()) { + return null; + } // Must downcase the username to find or save a ticket when // connected to a case insensitive server. String lowerCaseableUserName = userName; @@ -782,6 +785,11 @@ public String getAuthTicket(final String userName, final String serverId) { String serverAddress = serverId; if (isBlank(serverAddress)) { serverAddress = firstNonBlank(getServerId(), getServerAddress()); + + if (!authTickets.containsKey(composeAuthTicketEntryKey(lowerCaseableUserName, serverAddress))) { + serverAddress = getServerAddress(); + } + // Handling 'serverCluster' if (isClusterMember()) { serverAddress = serverInfo.getServerCluster(); diff --git a/src/main/java/com/perforce/p4java/impl/mapbased/rpc/func/client/ClientUserInteraction.java b/src/main/java/com/perforce/p4java/impl/mapbased/rpc/func/client/ClientUserInteraction.java index 728d13d..78495a6 100644 --- a/src/main/java/com/perforce/p4java/impl/mapbased/rpc/func/client/ClientUserInteraction.java +++ b/src/main/java/com/perforce/p4java/impl/mapbased/rpc/func/client/ClientUserInteraction.java @@ -564,13 +564,11 @@ protected RpcPacketDispatcherResult clientAck(RpcConnection rpcConnection, Comma if (handler != null) { if (handler.isError()) { confirm = decline; - } - } else { - - // no errors, if syncTime is set, send it - - if (cmdEnv.getSyncTime() != 0) { - resultsMap.put(RpcFunctionMapKey.SYNCTIME, cmdEnv.getSyncTime()); + } else { + // no errors, if syncTime is set, send it + if (cmdEnv.getSyncTime() != 0) { + resultsMap.put(RpcFunctionMapKey.SYNCTIME, String.valueOf(cmdEnv.getSyncTime())); + } } } diff --git a/src/main/java/com/perforce/p4java/impl/mapbased/rpc/func/helper/MapUnmapper.java b/src/main/java/com/perforce/p4java/impl/mapbased/rpc/func/helper/MapUnmapper.java index b5b652e..52b2c01 100644 --- a/src/main/java/com/perforce/p4java/impl/mapbased/rpc/func/helper/MapUnmapper.java +++ b/src/main/java/com/perforce/p4java/impl/mapbased/rpc/func/helper/MapUnmapper.java @@ -480,6 +480,9 @@ public static void unmapUserGroupMap(Map inMap, StringBuffer str if (inMap.containsKey(MapKeys.MAXOPENFILES_KEY)) { strBuf.append(MapKeys.MAXOPENFILES_KEY + MapKeys.COLON_SPACE + inMap.get(MapKeys.MAXOPENFILES_KEY) + MapKeys.DOUBLE_LF); } + if (inMap.containsKey(MapKeys.MAX_MEMORY_KEY)) { + strBuf.append(MapKeys.MAX_MEMORY_KEY + MapKeys.COLON_SPACE + inMap.get(MapKeys.MAX_MEMORY_KEY) + MapKeys.DOUBLE_LF); + } for (int i = 0; ; i++) { String mapStr = (String) inMap.get(MapKeys.SUBGROUPS_KEY + i); diff --git a/src/main/java/com/perforce/p4java/impl/mapbased/rpc/sys/helper/AppleFileHelper.java b/src/main/java/com/perforce/p4java/impl/mapbased/rpc/sys/helper/AppleFileHelper.java index 63f9268..75e9be6 100755 --- a/src/main/java/com/perforce/p4java/impl/mapbased/rpc/sys/helper/AppleFileHelper.java +++ b/src/main/java/com/perforce/p4java/impl/mapbased/rpc/sys/helper/AppleFileHelper.java @@ -1,118 +1,118 @@ -/** - * Copyright 2012 Perforce Software Inc., All Rights Reserved. - */ -package com.perforce.p4java.impl.mapbased.rpc.sys.helper; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; - -import com.perforce.p4java.Log; -import com.perforce.p4java.exception.FileDecoderException; -import com.perforce.p4java.impl.mapbased.rpc.sys.RpcPerforceFile; -import com.perforce.p4java.impl.mapbased.rpc.sys.RpcPerforceFileType; -import com.perforce.p4java.io.apple.AppleFileData; -import com.perforce.p4java.io.apple.AppleFileDecoder; - -/** - * Helper class for handling Apple files. - */ -public class AppleFileHelper { - - /** - * Extract the data fork and the resource fork from the Apple file. - * - * @param file the Apple file - */ - public static void extractFile(RpcPerforceFile file) { - if (file.getFileType() == RpcPerforceFileType.FST_APPLEFILE) { - FileOutputStream fosData = null; - FileOutputStream fosResource = null; - try { - byte[] data = AppleFileHelper.getBytesFromFile(file); - AppleFileData fileData = new AppleFileData(data); - AppleFileDecoder appleFile = new AppleFileDecoder(fileData); - appleFile.extract(); - fosData = new FileOutputStream(file); - AppleFileData forkData = appleFile.getDataFork(); - if (forkData != AppleFileData.EMPTY_FILE_DATA) { - fosData.write(forkData.getBytes()); - } - String resourceFilePath = file.getParent() + File.separator + "%" + file.getName(); - RpcPerforceFile targetResourceFile = new RpcPerforceFile(resourceFilePath, file.getFileType()); - fosResource = new FileOutputStream(targetResourceFile); - AppleFileData forkResource = appleFile.getResourceFork(); - if (forkResource != AppleFileData.EMPTY_FILE_DATA) { - fosResource.write(forkResource.getBytes()); - } - } catch (IOException e) { - Log.error("Problem handling the Apple file: " + file.getName()); - } catch (FileDecoderException e) { - Log.error("Problem decoding the Apple file: " + file.getName()); - } finally { - if (fosData != null) { - try { - fosData.close(); - } catch (Exception e) { - // Do nothing - } - } - if (fosResource != null) { - try { - fosResource.close(); - } catch (Exception e) { - // Do nothing - } - } - } - } - } - - /** - * Gets the bytes from file. - * - * @param file - * the file - * @return the bytes from file - * @throws IOException - * Signals that an I/O exception has occurred. - */ - public static byte[] getBytesFromFile(File file) throws IOException { - InputStream is = new FileInputStream(file); - - long length = file.length(); - if (length > Integer.MAX_VALUE) { - // File is too large - throw new IOException("Apple file too large for decoding."); - } - - byte[] bytes = new byte[(int) length]; - int offset = 0; - int numRead = 0; - - try { - while (offset < bytes.length - && (numRead = is.read(bytes, offset, bytes.length - offset)) >= 0) { - offset += numRead; - } - // Ensure all the bytes have been read in - if (offset < bytes.length) { - throw new IOException( - "Could not completely read the Apple file " - + file.getName()); - } - } finally { - if (is != null) { - try { - is.close(); - } catch (IOException e) { - // Do nothing - } - } - } - - return bytes; - } -} +/** + * Copyright 2012 Perforce Software Inc., All Rights Reserved. + */ +package com.perforce.p4java.impl.mapbased.rpc.sys.helper; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; + +import com.perforce.p4java.Log; +import com.perforce.p4java.exception.FileDecoderException; +import com.perforce.p4java.impl.mapbased.rpc.sys.RpcPerforceFile; +import com.perforce.p4java.impl.mapbased.rpc.sys.RpcPerforceFileType; +import com.perforce.p4java.io.apple.AppleFileData; +import com.perforce.p4java.io.apple.AppleFileDecoder; + +/** + * Helper class for handling Apple files. + */ +public class AppleFileHelper { + + /** + * Extract the data fork and the resource fork from the Apple file. + * + * @param file the Apple file + */ + public static void extractFile(RpcPerforceFile file) { + if (file.getFileType() == RpcPerforceFileType.FST_APPLEFILE) { + FileOutputStream fosData = null; + FileOutputStream fosResource = null; + try { + byte[] data = AppleFileHelper.getBytesFromFile(file); + AppleFileData fileData = new AppleFileData(data); + AppleFileDecoder appleFile = new AppleFileDecoder(fileData); + appleFile.extract(); + fosData = new FileOutputStream(file); + AppleFileData forkData = appleFile.getDataFork(); + if (forkData != AppleFileData.EMPTY_FILE_DATA) { + fosData.write(forkData.getBytes()); + } + String resourceFilePath = file.getParent() + File.separator + "%" + file.getName(); + RpcPerforceFile targetResourceFile = new RpcPerforceFile(resourceFilePath, file.getFileType()); + fosResource = new FileOutputStream(targetResourceFile); + AppleFileData forkResource = appleFile.getResourceFork(); + if (forkResource != AppleFileData.EMPTY_FILE_DATA) { + fosResource.write(forkResource.getBytes()); + } + } catch (IOException e) { + Log.error("Problem handling the Apple file: " + file.getName()); + } catch (FileDecoderException e) { + Log.error("Problem decoding the Apple file: " + file.getName()); + } finally { + if (fosData != null) { + try { + fosData.close(); + } catch (Exception e) { + // Do nothing + } + } + if (fosResource != null) { + try { + fosResource.close(); + } catch (Exception e) { + // Do nothing + } + } + } + } + } + + /** + * Gets the bytes from file. + * + * @param file + * the file + * @return the bytes from file + * @throws IOException + * Signals that an I/O exception has occurred. + */ + public static byte[] getBytesFromFile(File file) throws IOException { + InputStream is = new FileInputStream(file); + + long length = file.length(); + if (length > Integer.MAX_VALUE) { + // File is too large + throw new IOException("Apple file too large for decoding."); + } + + byte[] bytes = new byte[(int) length]; + int offset = 0; + int numRead = 0; + + try { + while (offset < bytes.length + && (numRead = is.read(bytes, offset, bytes.length - offset)) >= 0) { + offset += numRead; + } + // Ensure all the bytes have been read in + if (offset < bytes.length) { + throw new IOException( + "Could not completely read the Apple file " + + file.getName()); + } + } finally { + if (is != null) { + try { + is.close(); + } catch (IOException e) { + // Do nothing + } + } + } + + return bytes; + } +} diff --git a/src/main/java/com/perforce/p4java/impl/mapbased/rpc/sys/helper/Utf8ByteHelper.java b/src/main/java/com/perforce/p4java/impl/mapbased/rpc/sys/helper/Utf8ByteHelper.java index add3c86..fbd6328 100644 --- a/src/main/java/com/perforce/p4java/impl/mapbased/rpc/sys/helper/Utf8ByteHelper.java +++ b/src/main/java/com/perforce/p4java/impl/mapbased/rpc/sys/helper/Utf8ByteHelper.java @@ -46,7 +46,7 @@ public static int length(byte value) { } public static int findBufferLimit(ByteBuffer buffer) throws FileDecoderException { - // find start of multi byte + // find start of multibyte int r = buffer.remaining(); for (int i = 1; i <= 4; i++) { int pos = r - i; @@ -54,6 +54,11 @@ public static int findBufferLimit(ByteBuffer buffer) throws FileDecoderException Utf8ByteHelper t = Utf8ByteHelper.parse(b); switch (t) { case START: + int bytesInCharacter = Utf8ByteHelper.length(b) + 1; + // If multibyte character can be accommodated in the same buffer, add it. + if (pos + bytesInCharacter <= r) { + return pos + bytesInCharacter; + } return pos; case SINGLE: throw new FileDecoderException("Corrupt UTF8; single byte in multi byte sequence."); diff --git a/src/main/java/com/perforce/p4java/impl/mapbased/server/cmd/GroupDelegator.java b/src/main/java/com/perforce/p4java/impl/mapbased/server/cmd/GroupDelegator.java index 24f8f3e..ec506f5 100644 --- a/src/main/java/com/perforce/p4java/impl/mapbased/server/cmd/GroupDelegator.java +++ b/src/main/java/com/perforce/p4java/impl/mapbased/server/cmd/GroupDelegator.java @@ -74,8 +74,9 @@ public String createUserGroup(@Nonnull final IUserGroup group, @Nullable final U public String updateUserGroup(@Nonnull final IUserGroup group, @Nullable final UpdateUserGroupOptions opts) throws P4JavaException { Validate.notNull(group); - - List> resultMaps = execMapCmdList(GROUP, Parameters.processParameters(opts, null, "-i", server), InputMapper.map(group)); + List> resultMaps = execMapCmdList(GROUP, + Parameters.processParameters(opts, null, "-i", server), + InputMapper.map(group, server)); return ResultMapParser.parseCommandResultMapAsString(resultMaps); } diff --git a/src/main/java/com/perforce/p4java/io/apple/AppleFile.java b/src/main/java/com/perforce/p4java/io/apple/AppleFile.java index d6549f6..14a3c94 100644 --- a/src/main/java/com/perforce/p4java/io/apple/AppleFile.java +++ b/src/main/java/com/perforce/p4java/io/apple/AppleFile.java @@ -1,749 +1,749 @@ -/** - * Copyright 2012 Perforce Software Inc., All Rights Reserved. - */ -package com.perforce.p4java.io.apple; - -import com.perforce.p4java.Log; -import com.perforce.p4java.exception.FileDecoderException; - -/** - * This abstract class handles AppleSingle/Double files. It contains a common - * method to verify the Apple file, and figure out if it is an AppleSingle or - * AppleDouble formatted file. - *

- * The AppleSingle format is a representation of Macintosh files as one - * consecutive stream of bytes. AppleSingle combines the data fork, resource - * fork and the related Finder meta-file information into a single file. - *

- * The AppleDouble format stores the data fork, resource fork as two separate - * files. AppleDouble leaves the data fork in its original format, and the - * resource fork and Finder information were combined into a second file. - *

- * Apple defined the magic number for the AppleSingle format as 0x00051600, and - * the magic number for the AppleDouble format as 0x00051607. - * - *


- * AppleSingle file header:

- *

- * Field Length

- * ----- ------

- * Magic number -------- 4 bytes

- * Version number ------ 4 bytes

- * Filler ------------- 16 bytes

- * Number of entries --- 2 bytes

- *

- * Entry descriptor for each entry:

- * Entry ID ------ 4 bytes

- * Offset -------- 4 bytes

- * Length -------- 4 bytes

- *

- * Apple reserved entry IDs:

- *

- * Data Fork -------------- 1 Data fork

- * Resource Fork ---------- 2 Resource fork

- * Real Name -------------- 3 File's name as created on home file system

- * Comment ---------------- 4 Standard Macintosh comment

- * Icon, B and W ---------- 5 Standard Macintosh black and white icon

- * Icon, Color ------------ 6 Macintosh color icon

- * File Dates Info -------- 8 File creation date, modification date, and so on

- * Finder Info ------------ 9 Standard Macintosh Finder information

- * Macintosh File Info --- 10 Macintosh file information, attributes, and so on

- * ProDOS File Info ------ 11 ProDOS file information, attributes, and so on

- * MS-DOS File Info ------ 12 MS-DOS file information, attributes, and so on

- * Short Name ------------ 13 AFP short name

- * AFP File Info --------- 14 AFP file information, attributes, and so on

- * Directory ID ---------- 15 AFP directory ID

- * 
- *

- * See RFC 1740 for reference: http://tools.ietf.org/html/rfc1740 - */ -public abstract class AppleFile { - - /** - * The Apple file format: AppleSingle, AppleDouble, default to unknown. - */ - protected FileFormat format = FileFormat.UNKNOWN; - - /** - * The raw Apple file. - */ - protected AppleFileData fileData = AppleFileData.EMPTY_FILE_DATA; - - /** - * Entry 1: Data fork. - */ - protected AppleFileData dataFork = AppleFileData.EMPTY_FILE_DATA; - - /** - * Entry 2: Resource fork. - */ - protected AppleFileData resourceFork = AppleFileData.EMPTY_FILE_DATA; - - /** - * Entry 3: File's name as created on home file system. - */ - protected AppleFileData realName = AppleFileData.EMPTY_FILE_DATA; - - /** - * Entry 4: Standard Macintosh comment. - */ - protected AppleFileData comment = AppleFileData.EMPTY_FILE_DATA; - - /** - * Entry 5: Standard Macintosh black and white icon. - */ - protected AppleFileData iconBW = AppleFileData.EMPTY_FILE_DATA; - - /** - * Entry 6: Macintosh color icon. - */ - protected AppleFileData iconColor = AppleFileData.EMPTY_FILE_DATA; - - /** - * Entry 8: File creation date, modification date, and so on. - */ - protected AppleFileData fileDatesInfo = AppleFileData.EMPTY_FILE_DATA; - - /** - * The file dates info entry. - */ - protected FileDatesInfoEntry fileDatesInfoEntry = null; - - /** - * Entry 9: Standard Macintosh Finder information. - */ - protected AppleFileData finderInfo = AppleFileData.EMPTY_FILE_DATA; - - /** - * Entry 10: Macintosh file information, attributes, and so on. - */ - protected AppleFileData macintoshInfo = AppleFileData.EMPTY_FILE_DATA; - - /** - * Entry 11: ProDOS file information, attributes, and so on. - */ - protected AppleFileData proDOSFileInfo = AppleFileData.EMPTY_FILE_DATA; - - /** - * Entry 12: MS-DOS file information, attributes, and so on. - */ - protected AppleFileData msDOSFileInfo = AppleFileData.EMPTY_FILE_DATA; - - /** - * Entry 13: AFP short name. - */ - protected AppleFileData shortName = AppleFileData.EMPTY_FILE_DATA; - - /** - * Entry 14: AFP file information, attributes, and so on. - */ - protected AppleFileData afpFileInfo = AppleFileData.EMPTY_FILE_DATA; - - /** - * Entry 15: AFP directory ID. - */ - protected AppleFileData directoryID = AppleFileData.EMPTY_FILE_DATA; - - /** - * The num entries. - */ - protected int numEntries = 0; - - /** - * The Apple file format. - */ - public enum FileFormat { - - APPLE_SINGLE, APPLE_DOUBLE, UNKNOWN; - - /** - * Return a suitable Apple file format as inferred from the passed-in - * string. Otherwise return the UNKNOWN file format. - * - * @param fileFormat the file format - * @return the FileFormat - */ - public static FileFormat fromString(String fileFormat) { - if (fileFormat == null) { - return null; - } - - try { - return FileFormat.valueOf(fileFormat.toUpperCase()); - } catch (IllegalArgumentException iae) { - Log.error("Bad conversion attempt in FileFormat.fromString; string: " + fileFormat + "; message: " + iae.getMessage()); - Log.exception(iae); - return UNKNOWN; - } - } - } - - ; - - /** - * This class represents the file dates. - */ - public class FileDatesInfoEntry { - - /** - * The create time. - */ - private int createTime = Integer.MIN_VALUE; - - /** - * The modify time. - */ - private int modifyTime = Integer.MIN_VALUE; - - /** - * The backup time. - */ - private int backupTime = Integer.MIN_VALUE; - - /** - * The access time. - */ - private int accessTime = Integer.MIN_VALUE; - - /** - * Instantiates a new file dates info entry. - */ - public FileDatesInfoEntry() { - - } - - /** - * Gets the creates the time. - * - * @return the creates the time - */ - public int getCreateTime() { - return createTime; - } - - /** - * Sets the creates the time. - * - * @param createTime the new creates the time - */ - public void setCreateTime(int createTime) { - this.createTime = createTime; - } - - /** - * Gets the modify time. - * - * @return the modify time - */ - public int getModifyTime() { - return modifyTime; - } - - /** - * Sets the modify time. - * - * @param modifyTime the new modify time - */ - public void setModifyTime(int modifyTime) { - this.modifyTime = modifyTime; - } - - /** - * Gets the backup time. - * - * @return the backup time - */ - public int getBackupTime() { - return backupTime; - } - - /** - * Sets the backup time. - * - * @param backupTime the new backup time - */ - public void setBackupTime(int backupTime) { - this.backupTime = backupTime; - } - - /** - * Gets the access time. - * - * @return the access time - */ - public int getAccessTime() { - return accessTime; - } - - /** - * Sets the access time. - * - * @param accessTime the new access time - */ - public void setAccessTime(int accessTime) { - this.accessTime = accessTime; - } - } - - /** - * Sets the num entries. - * - * @param numEntries the new num entries - */ - public void setNumEntries(int numEntries) { - this.numEntries = numEntries; - } - - /** - * Verify the validity of the Apple file. - * - * @throws FileDecoderException the file decoder exception - */ - @SuppressWarnings("unused") - protected void verify() throws FileDecoderException { - byte[] data = this.fileData.getData(); - int offset = this.fileData.getOffset(); - int length = this.fileData.getLength(); - int position = offset; - if (length < 26) { - throw new FileDecoderException("File is too short"); - } - - /* Magic number */ - int magic = 0; - magic |= data[(position++)] & 0xFF; - magic <<= 8; - magic |= data[(position++)] & 0xFF; - magic <<= 8; - magic |= data[(position++)] & 0xFF; - magic <<= 8; - magic |= data[(position++)] & 0xFF; - - /* Check Apple file format: AppleSingle or AppleDobule */ - if (magic == 0x00051600) { - this.format = FileFormat.APPLE_SINGLE; - } else if (magic == 0x00051607) { - this.format = FileFormat.APPLE_DOUBLE; - } else { - throw new FileDecoderException("Invalid Apple file magic number."); - } - - /* Version number */ - int version = 0; - version |= data[(position++)] & 0xFF; - version <<= 8; - version |= data[(position++)] & 0xFF; - version <<= 8; - version |= data[(position++)] & 0xFF; - version <<= 8; - version |= data[(position++)] & 0xFF; - if (version != 0x00020000) { - throw new FileDecoderException("Unknown Apple file version"); - } - - /* Filler */ - position += 16; - - /* Number of entries */ - this.numEntries = 0; - this.numEntries |= data[(position++)] & 0xFF; - this.numEntries <<= 8; - this.numEntries |= data[(position++)] & 0xFF; - if (length < 26 + 12 * this.numEntries) { - throw new FileDecoderException("Corrupt Apple file data."); - } - - /* Check entries */ - int entryId = 0; - int entryOffset = 0; - int entryLength = 0; - int contentPosition = 26 + 12 * this.numEntries; - for (int i = 0; i < this.numEntries; i++) { - position = 26 + i * 12; - /* Entry ID */ - entryId = 0; - entryId |= data[(position++)] & 0xFF; - entryId <<= 8; - entryId |= data[(position++)] & 0xFF; - entryId <<= 8; - entryId |= data[(position++)] & 0xFF; - entryId <<= 8; - entryId |= data[(position++)] & 0xFF; - - /* Entry offset */ - entryOffset = 0; - entryOffset |= data[(position++)] & 0xFF; - entryOffset <<= 8; - entryOffset |= data[(position++)] & 0xFF; - entryOffset <<= 8; - entryOffset |= data[(position++)] & 0xFF; - entryOffset <<= 8; - entryOffset |= data[(position++)] & 0xFF; - entryOffset &= 0x7FFFFFFF; - - /* Entry length */ - entryLength = 0; - entryLength |= data[(position++)] & 0xFF; - entryLength <<= 8; - entryLength |= data[(position++)] & 0xFF; - entryLength <<= 8; - entryLength |= data[(position++)] & 0xFF; - entryLength <<= 8; - entryLength |= data[(position++)] & 0xFF; - entryLength &= 0x7FFFFFFF; - if ((entryOffset < contentPosition) || (length < entryOffset + entryLength)) { - throw new FileDecoderException("Corrupt Apple file data."); - } - } - } - - /** - * Extract file dates. - * - * @param data the data - * @param offset the offset - * @param length the length - */ - protected void extractFileDates(byte[] data, int offset, int length) { - if ((0 > offset) || (offset > data.length)) throw new IndexOutOfBoundsException(); - if ((0 > length) || (length > data.length - offset)) throw new IndexOutOfBoundsException(); - - int position = offset; - - int createTime = 0; - createTime |= data[(position++)] & 0xFF; - createTime <<= 8; - createTime |= data[(position++)] & 0xFF; - createTime <<= 8; - createTime |= data[(position++)] & 0xFF; - createTime <<= 8; - createTime |= data[(position++)] & 0xFF; - int modifyTime = 0; - modifyTime |= data[(position++)] & 0xFF; - modifyTime <<= 8; - modifyTime |= data[(position++)] & 0xFF; - modifyTime <<= 8; - modifyTime |= data[(position++)] & 0xFF; - modifyTime <<= 8; - modifyTime |= data[(position++)] & 0xFF; - int backupTime = 0; - backupTime |= data[(position++)] & 0xFF; - backupTime <<= 8; - backupTime |= data[(position++)] & 0xFF; - backupTime <<= 8; - backupTime |= data[(position++)] & 0xFF; - backupTime <<= 8; - backupTime |= data[(position++)] & 0xFF; - int accessTime = 0; - accessTime |= data[(position++)] & 0xFF; - accessTime <<= 8; - accessTime |= data[(position++)] & 0xFF; - accessTime <<= 8; - accessTime |= data[(position++)] & 0xFF; - accessTime <<= 8; - accessTime |= data[(position++)] & 0xFF; - - this.fileDatesInfoEntry = new FileDatesInfoEntry(); - fileDatesInfoEntry.setCreateTime(createTime); - fileDatesInfoEntry.setModifyTime(modifyTime); - fileDatesInfoEntry.setBackupTime(backupTime); - fileDatesInfoEntry.setAccessTime(accessTime); - } - - /** - * Gets the format. - * - * @return the format - */ - public FileFormat getFormat() { - return format; - } - - /** - * Sets the format. - * - * @param format the new format - */ - public void setFormat(FileFormat format) { - this.format = format; - } - - /** - * Gets the file data. - * - * @return the file data - */ - public AppleFileData getFileData() { - return fileData; - } - - /** - * Sets the file data. - * - * @param fileData the new file data - */ - public void setFileData(AppleFileData fileData) { - this.fileData = fileData; - } - - /** - * Gets the data fork. - * - * @return the data fork - */ - public AppleFileData getDataFork() { - return dataFork; - } - - /** - * Sets the data fork. - * - * @param dataFork the new data fork - */ - public void setDataFork(AppleFileData dataFork) { - this.dataFork = dataFork; - } - - /** - * Gets the resource fork. - * - * @return the resource fork - */ - public AppleFileData getResourceFork() { - return resourceFork; - } - - /** - * Sets the resource fork. - * - * @param resourceFork the new resource fork - */ - public void setResourceFork(AppleFileData resourceFork) { - this.resourceFork = resourceFork; - } - - /** - * Gets the real name. - * - * @return the real name - */ - public AppleFileData getRealName() { - return realName; - } - - /** - * Sets the real name. - * - * @param realName the new real name - */ - public void setRealName(AppleFileData realName) { - this.realName = realName; - } - - /** - * Gets the comment. - * - * @return the comment - */ - public AppleFileData getComment() { - return comment; - } - - /** - * Sets the comment. - * - * @param comment the new comment - */ - public void setComment(AppleFileData comment) { - this.comment = comment; - } - - /** - * Gets the icon bw. - * - * @return the icon bw - */ - public AppleFileData getIconBW() { - return iconBW; - } - - /** - * Sets the icon bw. - * - * @param iconBW the new icon bw - */ - public void setIconBW(AppleFileData iconBW) { - this.iconBW = iconBW; - } - - /** - * Gets the icon color. - * - * @return the icon color - */ - public AppleFileData getIconColor() { - return iconColor; - } - - /** - * Sets the icon color. - * - * @param iconColor the new icon color - */ - public void setIconColor(AppleFileData iconColor) { - this.iconColor = iconColor; - } - - /** - * Gets the file dates info. - * - * @return the file dates info - */ - public AppleFileData getFileDatesInfo() { - return fileDatesInfo; - } - - /** - * Sets the file dates info. - * - * @param fileDatesInfo the new file dates info - */ - public void setFileDatesInfo(AppleFileData fileDatesInfo) { - this.fileDatesInfo = fileDatesInfo; - } - - /** - * Gets the finder info. - * - * @return the finder info - */ - public AppleFileData getFinderInfo() { - return finderInfo; - } - - /** - * Sets the finder info. - * - * @param finderInfo the new finder info - */ - public void setFinderInfo(AppleFileData finderInfo) { - this.finderInfo = finderInfo; - } - - /** - * Gets the macintosh info. - * - * @return the macintosh info - */ - public AppleFileData getMacintoshInfo() { - return macintoshInfo; - } - - /** - * Sets the macintosh info. - * - * @param macintoshInfo the new macintosh info - */ - public void setMacintoshInfo(AppleFileData macintoshInfo) { - this.macintoshInfo = macintoshInfo; - } - - /** - * Gets the pro dos file info. - * - * @return the pro dos file info - */ - public AppleFileData getProDOSFileInfo() { - return proDOSFileInfo; - } - - /** - * Sets the pro dos file info. - * - * @param proDOSFileInfo the new pro dos file info - */ - public void setProDOSFileInfo(AppleFileData proDOSFileInfo) { - this.proDOSFileInfo = proDOSFileInfo; - } - - /** - * Gets the ms dos file info. - * - * @return the ms dos file info - */ - public AppleFileData getMsDOSFileInfo() { - return msDOSFileInfo; - } - - /** - * Sets the ms dos file info. - * - * @param msDOSFileInfo the new ms dos file info - */ - public void setMsDOSFileInfo(AppleFileData msDOSFileInfo) { - this.msDOSFileInfo = msDOSFileInfo; - } - - /** - * Gets the short name. - * - * @return the short name - */ - public AppleFileData getShortName() { - return shortName; - } - - /** - * Sets the short name. - * - * @param shortName the new short name - */ - public void setShortName(AppleFileData shortName) { - this.shortName = shortName; - } - - /** - * Gets the afp file info. - * - * @return the afp file info - */ - public AppleFileData getAfpFileInfo() { - return afpFileInfo; - } - - /** - * Sets the afp file info. - * - * @param afpFileInfo the new afp file info - */ - public void setAfpFileInfo(AppleFileData afpFileInfo) { - this.afpFileInfo = afpFileInfo; - } - - /** - * Gets the directory id. - * - * @return the directory id - */ - public AppleFileData getDirectoryID() { - return directoryID; - } - - /** - * Sets the directory id. - * - * @param directoryID the new directory id - */ - public void setDirectoryID(AppleFileData directoryID) { - this.directoryID = directoryID; - } - - /** - * Gets the num entries. - * - * @return the num entries - */ - public int getNumEntries() { - return numEntries; - } +/** + * Copyright 2012 Perforce Software Inc., All Rights Reserved. + */ +package com.perforce.p4java.io.apple; + +import com.perforce.p4java.Log; +import com.perforce.p4java.exception.FileDecoderException; + +/** + * This abstract class handles AppleSingle/Double files. It contains a common + * method to verify the Apple file, and figure out if it is an AppleSingle or + * AppleDouble formatted file. + *

+ * The AppleSingle format is a representation of Macintosh files as one + * consecutive stream of bytes. AppleSingle combines the data fork, resource + * fork and the related Finder meta-file information into a single file. + *

+ * The AppleDouble format stores the data fork, resource fork as two separate + * files. AppleDouble leaves the data fork in its original format, and the + * resource fork and Finder information were combined into a second file. + *

+ * Apple defined the magic number for the AppleSingle format as 0x00051600, and + * the magic number for the AppleDouble format as 0x00051607. + * + *

+ * AppleSingle file header:
+ *
+ * Field Length
+ * ----- ------
+ * Magic number -------- 4 bytes
+ * Version number ------ 4 bytes
+ * Filler ------------- 16 bytes
+ * Number of entries --- 2 bytes
+ *
+ * Entry descriptor for each entry:
+ * Entry ID ------ 4 bytes
+ * Offset -------- 4 bytes
+ * Length -------- 4 bytes
+ *
+ * Apple reserved entry IDs:
+ *
+ * Data Fork -------------- 1 Data fork
+ * Resource Fork ---------- 2 Resource fork
+ * Real Name -------------- 3 File's name as created on home file system
+ * Comment ---------------- 4 Standard Macintosh comment
+ * Icon, B and W ---------- 5 Standard Macintosh black and white icon
+ * Icon, Color ------------ 6 Macintosh color icon
+ * File Dates Info -------- 8 File creation date, modification date, and so on
+ * Finder Info ------------ 9 Standard Macintosh Finder information
+ * Macintosh File Info --- 10 Macintosh file information, attributes, and so on
+ * ProDOS File Info ------ 11 ProDOS file information, attributes, and so on
+ * MS-DOS File Info ------ 12 MS-DOS file information, attributes, and so on
+ * Short Name ------------ 13 AFP short name
+ * AFP File Info --------- 14 AFP file information, attributes, and so on
+ * Directory ID ---------- 15 AFP directory ID
+ * 
+ *

+ * See RFC 1740 for reference: http://tools.ietf.org/html/rfc1740 + */ +public abstract class AppleFile { + + /** + * The Apple file format: AppleSingle, AppleDouble, default to unknown. + */ + protected FileFormat format = FileFormat.UNKNOWN; + + /** + * The raw Apple file. + */ + protected AppleFileData fileData = AppleFileData.EMPTY_FILE_DATA; + + /** + * Entry 1: Data fork. + */ + protected AppleFileData dataFork = AppleFileData.EMPTY_FILE_DATA; + + /** + * Entry 2: Resource fork. + */ + protected AppleFileData resourceFork = AppleFileData.EMPTY_FILE_DATA; + + /** + * Entry 3: File's name as created on home file system. + */ + protected AppleFileData realName = AppleFileData.EMPTY_FILE_DATA; + + /** + * Entry 4: Standard Macintosh comment. + */ + protected AppleFileData comment = AppleFileData.EMPTY_FILE_DATA; + + /** + * Entry 5: Standard Macintosh black and white icon. + */ + protected AppleFileData iconBW = AppleFileData.EMPTY_FILE_DATA; + + /** + * Entry 6: Macintosh color icon. + */ + protected AppleFileData iconColor = AppleFileData.EMPTY_FILE_DATA; + + /** + * Entry 8: File creation date, modification date, and so on. + */ + protected AppleFileData fileDatesInfo = AppleFileData.EMPTY_FILE_DATA; + + /** + * The file dates info entry. + */ + protected FileDatesInfoEntry fileDatesInfoEntry = null; + + /** + * Entry 9: Standard Macintosh Finder information. + */ + protected AppleFileData finderInfo = AppleFileData.EMPTY_FILE_DATA; + + /** + * Entry 10: Macintosh file information, attributes, and so on. + */ + protected AppleFileData macintoshInfo = AppleFileData.EMPTY_FILE_DATA; + + /** + * Entry 11: ProDOS file information, attributes, and so on. + */ + protected AppleFileData proDOSFileInfo = AppleFileData.EMPTY_FILE_DATA; + + /** + * Entry 12: MS-DOS file information, attributes, and so on. + */ + protected AppleFileData msDOSFileInfo = AppleFileData.EMPTY_FILE_DATA; + + /** + * Entry 13: AFP short name. + */ + protected AppleFileData shortName = AppleFileData.EMPTY_FILE_DATA; + + /** + * Entry 14: AFP file information, attributes, and so on. + */ + protected AppleFileData afpFileInfo = AppleFileData.EMPTY_FILE_DATA; + + /** + * Entry 15: AFP directory ID. + */ + protected AppleFileData directoryID = AppleFileData.EMPTY_FILE_DATA; + + /** + * The num entries. + */ + protected int numEntries = 0; + + /** + * The Apple file format. + */ + public enum FileFormat { + + APPLE_SINGLE, APPLE_DOUBLE, UNKNOWN; + + /** + * Return a suitable Apple file format as inferred from the passed-in + * string. Otherwise return the UNKNOWN file format. + * + * @param fileFormat the file format + * @return the FileFormat + */ + public static FileFormat fromString(String fileFormat) { + if (fileFormat == null) { + return null; + } + + try { + return FileFormat.valueOf(fileFormat.toUpperCase()); + } catch (IllegalArgumentException iae) { + Log.error("Bad conversion attempt in FileFormat.fromString; string: " + fileFormat + "; message: " + iae.getMessage()); + Log.exception(iae); + return UNKNOWN; + } + } + } + + ; + + /** + * This class represents the file dates. + */ + public class FileDatesInfoEntry { + + /** + * The create time. + */ + private int createTime = Integer.MIN_VALUE; + + /** + * The modify time. + */ + private int modifyTime = Integer.MIN_VALUE; + + /** + * The backup time. + */ + private int backupTime = Integer.MIN_VALUE; + + /** + * The access time. + */ + private int accessTime = Integer.MIN_VALUE; + + /** + * Instantiates a new file dates info entry. + */ + public FileDatesInfoEntry() { + + } + + /** + * Gets the creates the time. + * + * @return the creates the time + */ + public int getCreateTime() { + return createTime; + } + + /** + * Sets the creates the time. + * + * @param createTime the new creates the time + */ + public void setCreateTime(int createTime) { + this.createTime = createTime; + } + + /** + * Gets the modify time. + * + * @return the modify time + */ + public int getModifyTime() { + return modifyTime; + } + + /** + * Sets the modify time. + * + * @param modifyTime the new modify time + */ + public void setModifyTime(int modifyTime) { + this.modifyTime = modifyTime; + } + + /** + * Gets the backup time. + * + * @return the backup time + */ + public int getBackupTime() { + return backupTime; + } + + /** + * Sets the backup time. + * + * @param backupTime the new backup time + */ + public void setBackupTime(int backupTime) { + this.backupTime = backupTime; + } + + /** + * Gets the access time. + * + * @return the access time + */ + public int getAccessTime() { + return accessTime; + } + + /** + * Sets the access time. + * + * @param accessTime the new access time + */ + public void setAccessTime(int accessTime) { + this.accessTime = accessTime; + } + } + + /** + * Sets the num entries. + * + * @param numEntries the new num entries + */ + public void setNumEntries(int numEntries) { + this.numEntries = numEntries; + } + + /** + * Verify the validity of the Apple file. + * + * @throws FileDecoderException the file decoder exception + */ + @SuppressWarnings("unused") + protected void verify() throws FileDecoderException { + byte[] data = this.fileData.getData(); + int offset = this.fileData.getOffset(); + int length = this.fileData.getLength(); + int position = offset; + if (length < 26) { + throw new FileDecoderException("File is too short"); + } + + /* Magic number */ + int magic = 0; + magic |= data[(position++)] & 0xFF; + magic <<= 8; + magic |= data[(position++)] & 0xFF; + magic <<= 8; + magic |= data[(position++)] & 0xFF; + magic <<= 8; + magic |= data[(position++)] & 0xFF; + + /* Check Apple file format: AppleSingle or AppleDobule */ + if (magic == 0x00051600) { + this.format = FileFormat.APPLE_SINGLE; + } else if (magic == 0x00051607) { + this.format = FileFormat.APPLE_DOUBLE; + } else { + throw new FileDecoderException("Invalid Apple file magic number."); + } + + /* Version number */ + int version = 0; + version |= data[(position++)] & 0xFF; + version <<= 8; + version |= data[(position++)] & 0xFF; + version <<= 8; + version |= data[(position++)] & 0xFF; + version <<= 8; + version |= data[(position++)] & 0xFF; + if (version != 0x00020000) { + throw new FileDecoderException("Unknown Apple file version"); + } + + /* Filler */ + position += 16; + + /* Number of entries */ + this.numEntries = 0; + this.numEntries |= data[(position++)] & 0xFF; + this.numEntries <<= 8; + this.numEntries |= data[(position++)] & 0xFF; + if (length < 26 + 12 * this.numEntries) { + throw new FileDecoderException("Corrupt Apple file data."); + } + + /* Check entries */ + int entryId = 0; + int entryOffset = 0; + int entryLength = 0; + int contentPosition = 26 + 12 * this.numEntries; + for (int i = 0; i < this.numEntries; i++) { + position = 26 + i * 12; + /* Entry ID */ + entryId = 0; + entryId |= data[(position++)] & 0xFF; + entryId <<= 8; + entryId |= data[(position++)] & 0xFF; + entryId <<= 8; + entryId |= data[(position++)] & 0xFF; + entryId <<= 8; + entryId |= data[(position++)] & 0xFF; + + /* Entry offset */ + entryOffset = 0; + entryOffset |= data[(position++)] & 0xFF; + entryOffset <<= 8; + entryOffset |= data[(position++)] & 0xFF; + entryOffset <<= 8; + entryOffset |= data[(position++)] & 0xFF; + entryOffset <<= 8; + entryOffset |= data[(position++)] & 0xFF; + entryOffset &= 0x7FFFFFFF; + + /* Entry length */ + entryLength = 0; + entryLength |= data[(position++)] & 0xFF; + entryLength <<= 8; + entryLength |= data[(position++)] & 0xFF; + entryLength <<= 8; + entryLength |= data[(position++)] & 0xFF; + entryLength <<= 8; + entryLength |= data[(position++)] & 0xFF; + entryLength &= 0x7FFFFFFF; + if ((entryOffset < contentPosition) || (length < entryOffset + entryLength)) { + throw new FileDecoderException("Corrupt Apple file data."); + } + } + } + + /** + * Extract file dates. + * + * @param data the data + * @param offset the offset + * @param length the length + */ + protected void extractFileDates(byte[] data, int offset, int length) { + if ((0 > offset) || (offset > data.length)) throw new IndexOutOfBoundsException(); + if ((0 > length) || (length > data.length - offset)) throw new IndexOutOfBoundsException(); + + int position = offset; + + int createTime = 0; + createTime |= data[(position++)] & 0xFF; + createTime <<= 8; + createTime |= data[(position++)] & 0xFF; + createTime <<= 8; + createTime |= data[(position++)] & 0xFF; + createTime <<= 8; + createTime |= data[(position++)] & 0xFF; + int modifyTime = 0; + modifyTime |= data[(position++)] & 0xFF; + modifyTime <<= 8; + modifyTime |= data[(position++)] & 0xFF; + modifyTime <<= 8; + modifyTime |= data[(position++)] & 0xFF; + modifyTime <<= 8; + modifyTime |= data[(position++)] & 0xFF; + int backupTime = 0; + backupTime |= data[(position++)] & 0xFF; + backupTime <<= 8; + backupTime |= data[(position++)] & 0xFF; + backupTime <<= 8; + backupTime |= data[(position++)] & 0xFF; + backupTime <<= 8; + backupTime |= data[(position++)] & 0xFF; + int accessTime = 0; + accessTime |= data[(position++)] & 0xFF; + accessTime <<= 8; + accessTime |= data[(position++)] & 0xFF; + accessTime <<= 8; + accessTime |= data[(position++)] & 0xFF; + accessTime <<= 8; + accessTime |= data[(position++)] & 0xFF; + + this.fileDatesInfoEntry = new FileDatesInfoEntry(); + fileDatesInfoEntry.setCreateTime(createTime); + fileDatesInfoEntry.setModifyTime(modifyTime); + fileDatesInfoEntry.setBackupTime(backupTime); + fileDatesInfoEntry.setAccessTime(accessTime); + } + + /** + * Gets the format. + * + * @return the format + */ + public FileFormat getFormat() { + return format; + } + + /** + * Sets the format. + * + * @param format the new format + */ + public void setFormat(FileFormat format) { + this.format = format; + } + + /** + * Gets the file data. + * + * @return the file data + */ + public AppleFileData getFileData() { + return fileData; + } + + /** + * Sets the file data. + * + * @param fileData the new file data + */ + public void setFileData(AppleFileData fileData) { + this.fileData = fileData; + } + + /** + * Gets the data fork. + * + * @return the data fork + */ + public AppleFileData getDataFork() { + return dataFork; + } + + /** + * Sets the data fork. + * + * @param dataFork the new data fork + */ + public void setDataFork(AppleFileData dataFork) { + this.dataFork = dataFork; + } + + /** + * Gets the resource fork. + * + * @return the resource fork + */ + public AppleFileData getResourceFork() { + return resourceFork; + } + + /** + * Sets the resource fork. + * + * @param resourceFork the new resource fork + */ + public void setResourceFork(AppleFileData resourceFork) { + this.resourceFork = resourceFork; + } + + /** + * Gets the real name. + * + * @return the real name + */ + public AppleFileData getRealName() { + return realName; + } + + /** + * Sets the real name. + * + * @param realName the new real name + */ + public void setRealName(AppleFileData realName) { + this.realName = realName; + } + + /** + * Gets the comment. + * + * @return the comment + */ + public AppleFileData getComment() { + return comment; + } + + /** + * Sets the comment. + * + * @param comment the new comment + */ + public void setComment(AppleFileData comment) { + this.comment = comment; + } + + /** + * Gets the icon bw. + * + * @return the icon bw + */ + public AppleFileData getIconBW() { + return iconBW; + } + + /** + * Sets the icon bw. + * + * @param iconBW the new icon bw + */ + public void setIconBW(AppleFileData iconBW) { + this.iconBW = iconBW; + } + + /** + * Gets the icon color. + * + * @return the icon color + */ + public AppleFileData getIconColor() { + return iconColor; + } + + /** + * Sets the icon color. + * + * @param iconColor the new icon color + */ + public void setIconColor(AppleFileData iconColor) { + this.iconColor = iconColor; + } + + /** + * Gets the file dates info. + * + * @return the file dates info + */ + public AppleFileData getFileDatesInfo() { + return fileDatesInfo; + } + + /** + * Sets the file dates info. + * + * @param fileDatesInfo the new file dates info + */ + public void setFileDatesInfo(AppleFileData fileDatesInfo) { + this.fileDatesInfo = fileDatesInfo; + } + + /** + * Gets the finder info. + * + * @return the finder info + */ + public AppleFileData getFinderInfo() { + return finderInfo; + } + + /** + * Sets the finder info. + * + * @param finderInfo the new finder info + */ + public void setFinderInfo(AppleFileData finderInfo) { + this.finderInfo = finderInfo; + } + + /** + * Gets the macintosh info. + * + * @return the macintosh info + */ + public AppleFileData getMacintoshInfo() { + return macintoshInfo; + } + + /** + * Sets the macintosh info. + * + * @param macintoshInfo the new macintosh info + */ + public void setMacintoshInfo(AppleFileData macintoshInfo) { + this.macintoshInfo = macintoshInfo; + } + + /** + * Gets the pro dos file info. + * + * @return the pro dos file info + */ + public AppleFileData getProDOSFileInfo() { + return proDOSFileInfo; + } + + /** + * Sets the pro dos file info. + * + * @param proDOSFileInfo the new pro dos file info + */ + public void setProDOSFileInfo(AppleFileData proDOSFileInfo) { + this.proDOSFileInfo = proDOSFileInfo; + } + + /** + * Gets the ms dos file info. + * + * @return the ms dos file info + */ + public AppleFileData getMsDOSFileInfo() { + return msDOSFileInfo; + } + + /** + * Sets the ms dos file info. + * + * @param msDOSFileInfo the new ms dos file info + */ + public void setMsDOSFileInfo(AppleFileData msDOSFileInfo) { + this.msDOSFileInfo = msDOSFileInfo; + } + + /** + * Gets the short name. + * + * @return the short name + */ + public AppleFileData getShortName() { + return shortName; + } + + /** + * Sets the short name. + * + * @param shortName the new short name + */ + public void setShortName(AppleFileData shortName) { + this.shortName = shortName; + } + + /** + * Gets the afp file info. + * + * @return the afp file info + */ + public AppleFileData getAfpFileInfo() { + return afpFileInfo; + } + + /** + * Sets the afp file info. + * + * @param afpFileInfo the new afp file info + */ + public void setAfpFileInfo(AppleFileData afpFileInfo) { + this.afpFileInfo = afpFileInfo; + } + + /** + * Gets the directory id. + * + * @return the directory id + */ + public AppleFileData getDirectoryID() { + return directoryID; + } + + /** + * Sets the directory id. + * + * @param directoryID the new directory id + */ + public void setDirectoryID(AppleFileData directoryID) { + this.directoryID = directoryID; + } + + /** + * Gets the num entries. + * + * @return the num entries + */ + public int getNumEntries() { + return numEntries; + } } \ No newline at end of file diff --git a/src/main/java/com/perforce/p4java/io/apple/AppleFileData.java b/src/main/java/com/perforce/p4java/io/apple/AppleFileData.java index 517d031..d89471e 100755 --- a/src/main/java/com/perforce/p4java/io/apple/AppleFileData.java +++ b/src/main/java/com/perforce/p4java/io/apple/AppleFileData.java @@ -1,91 +1,91 @@ -/** - * Copyright 2012 Perforce Software Inc., All Rights Reserved. - */ -package com.perforce.p4java.io.apple; - -/** - * This class is for representing the AppleSingle/Double file or its file forks - * (data fork and resource fork) and the related Finder meta-file information. - */ -public final class AppleFileData { - - public static final AppleFileData EMPTY_FILE_DATA = new AppleFileData(); - private byte[] data; - private int offset; - private int length; - - /** - * Instantiates a new apple file data. - */ - public AppleFileData() { - this.data = new byte[0]; - this.offset = 0; - this.length = 0; - } - - /** - * Instantiates a new apple file data. - * - * @param data the data - */ - public AppleFileData(byte[] data) { - this.data = data; - this.offset = 0; - this.length = data.length; - } - - /** - * Instantiates a new apple file data. - * - * @param data the data - * @param offset the offset - * @param length the length - */ - public AppleFileData(byte[] data, int offset, int length) { - if ((0 > offset) || (offset > data.length)) - throw new IndexOutOfBoundsException(); - if ((0 > length) || (length > data.length - offset)) - throw new IndexOutOfBoundsException(); - this.data = data; - this.offset = offset; - this.length = length; - } - - /** - * Gets the bytes. - * - * @return the bytes - */ - public byte[] getBytes() { - byte[] data = new byte[this.length]; - System.arraycopy(this.data, this.offset, data, 0, this.length); - return data; - } - - /** - * Gets the data. - * - * @return the data - */ - public byte[] getData() { - return this.data; - } - - /** - * Gets the offset. - * - * @return the offset - */ - public int getOffset() { - return this.offset; - } - - /** - * Gets the length. - * - * @return the length - */ - public int getLength() { - return this.length; - } +/** + * Copyright 2012 Perforce Software Inc., All Rights Reserved. + */ +package com.perforce.p4java.io.apple; + +/** + * This class is for representing the AppleSingle/Double file or its file forks + * (data fork and resource fork) and the related Finder meta-file information. + */ +public final class AppleFileData { + + public static final AppleFileData EMPTY_FILE_DATA = new AppleFileData(); + private byte[] data; + private int offset; + private int length; + + /** + * Instantiates a new apple file data. + */ + public AppleFileData() { + this.data = new byte[0]; + this.offset = 0; + this.length = 0; + } + + /** + * Instantiates a new apple file data. + * + * @param data the data + */ + public AppleFileData(byte[] data) { + this.data = data; + this.offset = 0; + this.length = data.length; + } + + /** + * Instantiates a new apple file data. + * + * @param data the data + * @param offset the offset + * @param length the length + */ + public AppleFileData(byte[] data, int offset, int length) { + if ((0 > offset) || (offset > data.length)) + throw new IndexOutOfBoundsException(); + if ((0 > length) || (length > data.length - offset)) + throw new IndexOutOfBoundsException(); + this.data = data; + this.offset = offset; + this.length = length; + } + + /** + * Gets the bytes. + * + * @return the bytes + */ + public byte[] getBytes() { + byte[] data = new byte[this.length]; + System.arraycopy(this.data, this.offset, data, 0, this.length); + return data; + } + + /** + * Gets the data. + * + * @return the data + */ + public byte[] getData() { + return this.data; + } + + /** + * Gets the offset. + * + * @return the offset + */ + public int getOffset() { + return this.offset; + } + + /** + * Gets the length. + * + * @return the length + */ + public int getLength() { + return this.length; + } } \ No newline at end of file diff --git a/src/main/java/com/perforce/p4java/io/apple/AppleFileDecoder.java b/src/main/java/com/perforce/p4java/io/apple/AppleFileDecoder.java index 206a13d..aa7b036 100755 --- a/src/main/java/com/perforce/p4java/io/apple/AppleFileDecoder.java +++ b/src/main/java/com/perforce/p4java/io/apple/AppleFileDecoder.java @@ -1,143 +1,143 @@ -/** - * Copyright 2012 Perforce Software Inc., All Rights Reserved. - */ -package com.perforce.p4java.io.apple; - -import com.perforce.p4java.Log; -import com.perforce.p4java.exception.FileDecoderException; - -/** - * This class handles the extraction of the data fork, resource fork and other - * entries from an AppleSingle/Double file. The Perforce 'apple' file type is a - * compressed AppleSingle (Mac resource + data) file. The Perforce 'resource' - * file type is a compressed AppleDouble (Mac resource fork) file. - */ -public class AppleFileDecoder extends AppleFile { - - /** - * Instantiates a new apple file decoder. - * - * @param fileData - * the file data - */ - public AppleFileDecoder(AppleFileData fileData) { - if (fileData != null) { - this.fileData = fileData; - } - } - - /** - * Extract the data fork, resource fork and other entries from the Apple - * file. - * - * @throws FileDecoderException - * the file decoder exception - */ - @SuppressWarnings("unused") - public void extract() throws FileDecoderException { - // Verify the validity of the Apple file - verify(); - - byte[] data = this.fileData.getData(); - int offset = this.fileData.getOffset(); - int length = this.fileData.getLength(); - int contentPosition = 0; - int entryId = 0; - int entryOffset = 0; - int entryLength = 0; - for (int i = 0; i < this.numEntries; i++) { - contentPosition = offset + 26 + i * 12; - /* Entry ID */ - entryId = 0; - entryId |= data[(contentPosition++)] & 0xFF; - entryId <<= 8; - entryId |= data[(contentPosition++)] & 0xFF; - entryId <<= 8; - entryId |= data[(contentPosition++)] & 0xFF; - entryId <<= 8; - entryId |= data[(contentPosition++)] & 0xFF; - - /* Entry offset */ - entryOffset = 0; - entryOffset |= data[(contentPosition++)] & 0xFF; - entryOffset <<= 8; - entryOffset |= data[(contentPosition++)] & 0xFF; - entryOffset <<= 8; - entryOffset |= data[(contentPosition++)] & 0xFF; - entryOffset <<= 8; - entryOffset |= data[(contentPosition++)] & 0xFF; - entryOffset &= 0x7FFFFFFF; - - /* Entry length */ - entryLength = 0; - entryLength |= data[(contentPosition++)] & 0xFF; - entryLength <<= 8; - entryLength |= data[(contentPosition++)] & 0xFF; - entryLength <<= 8; - entryLength |= data[(contentPosition++)] & 0xFF; - entryLength <<= 8; - entryLength |= data[(contentPosition++)] & 0xFF; - entryLength &= 0x7FFFFFFF; - - switch (entryId) { - case 1: - this.dataFork = new AppleFileData(data, offset + entryOffset, - entryLength); - break; - case 2: - this.resourceFork = new AppleFileData(data, offset - + entryOffset, entryLength); - break; - case 3: - this.realName = new AppleFileData(data, offset - + entryOffset, entryLength); - break; - case 4: - this.comment = new AppleFileData(data, offset - + entryOffset, entryLength); - break; - case 5: - this.iconBW = new AppleFileData(data, offset - + entryOffset, entryLength); - break; - case 6: - this.iconColor = new AppleFileData(data, offset - + entryOffset, entryLength); - break; - case 8: - this.fileDatesInfo = new AppleFileData(data, offset - + entryOffset, entryLength); - extractFileDates(data, offset + entryOffset, entryLength); - break; - case 9: - this.finderInfo = new AppleFileData(data, offset - + entryOffset, entryLength); - break; - case 10: - this.macintoshInfo = new AppleFileData(data, offset - + entryOffset, entryLength); - case 11: - this.proDOSFileInfo = new AppleFileData(data, offset - + entryOffset, entryLength); - case 12: - this.msDOSFileInfo = new AppleFileData(data, offset - + entryOffset, entryLength); - case 13: - this.shortName = new AppleFileData(data, offset - + entryOffset, entryLength); - break; - case 14: - this.afpFileInfo = new AppleFileData(data, offset - + entryOffset, entryLength); - break; - case 15: - this.directoryID = new AppleFileData(data, offset - + entryOffset, entryLength); - break; - default: - Log.warn("Apple file entry ID: " + entryId + " is not handled."); - - } - } - } +/** + * Copyright 2012 Perforce Software Inc., All Rights Reserved. + */ +package com.perforce.p4java.io.apple; + +import com.perforce.p4java.Log; +import com.perforce.p4java.exception.FileDecoderException; + +/** + * This class handles the extraction of the data fork, resource fork and other + * entries from an AppleSingle/Double file. The Perforce 'apple' file type is a + * compressed AppleSingle (Mac resource + data) file. The Perforce 'resource' + * file type is a compressed AppleDouble (Mac resource fork) file. + */ +public class AppleFileDecoder extends AppleFile { + + /** + * Instantiates a new apple file decoder. + * + * @param fileData + * the file data + */ + public AppleFileDecoder(AppleFileData fileData) { + if (fileData != null) { + this.fileData = fileData; + } + } + + /** + * Extract the data fork, resource fork and other entries from the Apple + * file. + * + * @throws FileDecoderException + * the file decoder exception + */ + @SuppressWarnings("unused") + public void extract() throws FileDecoderException { + // Verify the validity of the Apple file + verify(); + + byte[] data = this.fileData.getData(); + int offset = this.fileData.getOffset(); + int length = this.fileData.getLength(); + int contentPosition = 0; + int entryId = 0; + int entryOffset = 0; + int entryLength = 0; + for (int i = 0; i < this.numEntries; i++) { + contentPosition = offset + 26 + i * 12; + /* Entry ID */ + entryId = 0; + entryId |= data[(contentPosition++)] & 0xFF; + entryId <<= 8; + entryId |= data[(contentPosition++)] & 0xFF; + entryId <<= 8; + entryId |= data[(contentPosition++)] & 0xFF; + entryId <<= 8; + entryId |= data[(contentPosition++)] & 0xFF; + + /* Entry offset */ + entryOffset = 0; + entryOffset |= data[(contentPosition++)] & 0xFF; + entryOffset <<= 8; + entryOffset |= data[(contentPosition++)] & 0xFF; + entryOffset <<= 8; + entryOffset |= data[(contentPosition++)] & 0xFF; + entryOffset <<= 8; + entryOffset |= data[(contentPosition++)] & 0xFF; + entryOffset &= 0x7FFFFFFF; + + /* Entry length */ + entryLength = 0; + entryLength |= data[(contentPosition++)] & 0xFF; + entryLength <<= 8; + entryLength |= data[(contentPosition++)] & 0xFF; + entryLength <<= 8; + entryLength |= data[(contentPosition++)] & 0xFF; + entryLength <<= 8; + entryLength |= data[(contentPosition++)] & 0xFF; + entryLength &= 0x7FFFFFFF; + + switch (entryId) { + case 1: + this.dataFork = new AppleFileData(data, offset + entryOffset, + entryLength); + break; + case 2: + this.resourceFork = new AppleFileData(data, offset + + entryOffset, entryLength); + break; + case 3: + this.realName = new AppleFileData(data, offset + + entryOffset, entryLength); + break; + case 4: + this.comment = new AppleFileData(data, offset + + entryOffset, entryLength); + break; + case 5: + this.iconBW = new AppleFileData(data, offset + + entryOffset, entryLength); + break; + case 6: + this.iconColor = new AppleFileData(data, offset + + entryOffset, entryLength); + break; + case 8: + this.fileDatesInfo = new AppleFileData(data, offset + + entryOffset, entryLength); + extractFileDates(data, offset + entryOffset, entryLength); + break; + case 9: + this.finderInfo = new AppleFileData(data, offset + + entryOffset, entryLength); + break; + case 10: + this.macintoshInfo = new AppleFileData(data, offset + + entryOffset, entryLength); + case 11: + this.proDOSFileInfo = new AppleFileData(data, offset + + entryOffset, entryLength); + case 12: + this.msDOSFileInfo = new AppleFileData(data, offset + + entryOffset, entryLength); + case 13: + this.shortName = new AppleFileData(data, offset + + entryOffset, entryLength); + break; + case 14: + this.afpFileInfo = new AppleFileData(data, offset + + entryOffset, entryLength); + break; + case 15: + this.directoryID = new AppleFileData(data, offset + + entryOffset, entryLength); + break; + default: + Log.warn("Apple file entry ID: " + entryId + " is not handled."); + + } + } + } } \ No newline at end of file diff --git a/src/main/java/com/perforce/p4java/io/apple/AppleFileEncoder.java b/src/main/java/com/perforce/p4java/io/apple/AppleFileEncoder.java index 72f2061..c13e239 100644 --- a/src/main/java/com/perforce/p4java/io/apple/AppleFileEncoder.java +++ b/src/main/java/com/perforce/p4java/io/apple/AppleFileEncoder.java @@ -1,299 +1,299 @@ -/** - * Copyright 2012 Perforce Software Inc., All Rights Reserved. - */ -package com.perforce.p4java.io.apple; - -import com.perforce.p4java.exception.FileEncoderException; - -/** - * This class handles the combination of the data fork, resource fork and other - * entries into an AppleSingle/Double file. - *

- * Note that if it is an AppleDouble, the data fork is a separate file external - * to this file. - */ -public class AppleFileEncoder extends AppleFile { - - /** - * Instantiates a new apple file decoder. - * - * @param fileFormat fileFormat - * @throws FileEncoderException on error - */ - public AppleFileEncoder(FileFormat fileFormat) throws FileEncoderException { - if (fileFormat == null) { - throw new FileEncoderException("Null file format passed to the AppleFileEncoder constructor."); - } - if (fileFormat == FileFormat.UNKNOWN) { - throw new FileEncoderException("Unknown file format passed to the AppleFileEncoder constructor."); - } - } - - /** - * Combine the data fork, resource fork and other entries into an - * AppleSingle/Double file. - * - * @throws FileEncoderException the file encoder exception - */ - @SuppressWarnings("unused") - public void combine() throws FileEncoderException { - - boolean isAppleSingle = (this.format == FileFormat.APPLE_SINGLE); - boolean isAppleDouble = (this.format == FileFormat.APPLE_DOUBLE); - - boolean hasDataFork = (this.dataFork != AppleFileData.EMPTY_FILE_DATA); - boolean hasResourceFork = (this.resourceFork != AppleFileData.EMPTY_FILE_DATA); - boolean hasRealName = (this.realName != AppleFileData.EMPTY_FILE_DATA); - boolean hasComment = (this.comment != AppleFileData.EMPTY_FILE_DATA); - boolean hasIconBW = (this.iconBW != AppleFileData.EMPTY_FILE_DATA); - boolean hasIconColor = (this.iconColor != AppleFileData.EMPTY_FILE_DATA); - boolean hasFileDatesInfo = (this.fileDatesInfoEntry != null); - boolean hasFinderInfo = (this.finderInfo != AppleFileData.EMPTY_FILE_DATA); - boolean hasMacintoshInfo = (this.macintoshInfo != AppleFileData.EMPTY_FILE_DATA); - boolean hasProDOSFileInfo = (this.proDOSFileInfo != AppleFileData.EMPTY_FILE_DATA); - boolean hasMsDOSFileInfo = (this.msDOSFileInfo != AppleFileData.EMPTY_FILE_DATA); - boolean hasShortName = (this.shortName != AppleFileData.EMPTY_FILE_DATA); - boolean hasAfpFileInfo = (this.afpFileInfo != AppleFileData.EMPTY_FILE_DATA); - boolean hasDirectoryID = (this.directoryID != AppleFileData.EMPTY_FILE_DATA); - - this.fileData = AppleFileData.EMPTY_FILE_DATA; - - int length = 90 + this.realName.getLength() - + this.resourceFork.getLength(); - - /* AppleSingle includes the data fork */ - if (isAppleSingle) { - length += this.dataFork.getLength(); - } - - byte[] data = new byte[length]; - int position = 0; - - /* Magic number for AppleSingle or AppleDouble */ - if (isAppleDouble) { - data[(position++)] = 0; - data[(position++)] = 5; - data[(position++)] = 22; - data[(position++)] = 0; - } else { - data[(position++)] = 0; - data[(position++)] = 5; - data[(position++)] = 22; - data[(position++)] = 7; - } - - /* Version number */ - data[(position++)] = 0; - data[(position++)] = 2; - data[(position++)] = 0; - data[(position++)] = 0; - - /* Filler */ - - for (int k = 0; k < 16; k++) { - data[(position++)] = 0; - } - - /* Number of entries */ - this.numEntries = 0; - if (hasRealName) { - this.numEntries += 1; - } - if (hasFileDatesInfo) { - this.numEntries += 1; - } - if (hasResourceFork) { - this.numEntries += 1; - } - if ((hasDataFork) && (isAppleSingle)) { - this.numEntries += 1; - } - data[(position++)] = ((byte) (this.numEntries >> 8 & 0xFF)); - data[(position++)] = ((byte) (this.numEntries & 0xFF)); - - /* Header information for the entries */ - - /* Real name entry header */ - int realNamePosition = 0; - if (hasRealName) { - int realNameEntryId = 3; - int realNameEntryOffset = 0; - int realNameEntryLength = this.realName.getLength(); - data[(position++)] = ((byte) (realNameEntryId >> 24 & 0xFF)); - data[(position++)] = ((byte) (realNameEntryId >> 16 & 0xFF)); - data[(position++)] = ((byte) (realNameEntryId >> 8 & 0xFF)); - data[(position++)] = ((byte) (realNameEntryId >> 0 & 0xFF)); - realNamePosition = position; - data[(position++)] = ((byte) (realNameEntryOffset >> 24 & 0xFF)); - data[(position++)] = ((byte) (realNameEntryOffset >> 16 & 0xFF)); - data[(position++)] = ((byte) (realNameEntryOffset >> 8 & 0xFF)); - data[(position++)] = ((byte) (realNameEntryOffset >> 0 & 0xFF)); - data[(position++)] = ((byte) (realNameEntryLength >> 24 & 0xFF)); - data[(position++)] = ((byte) (realNameEntryLength >> 16 & 0xFF)); - data[(position++)] = ((byte) (realNameEntryLength >> 8 & 0xFF)); - data[(position++)] = ((byte) (realNameEntryLength >> 0 & 0xFF)); - } - - /* File dates info entry header */ - int fileDatesInfoPosition = 0; - if (hasFileDatesInfo) { - int fileDatesInfoEntryId = 8; - int fileDatesInfoEntryOffset = 0; - int fileDatesInfoEntryLength = 16; - data[(position++)] = ((byte) (fileDatesInfoEntryId >> 24 & 0xFF)); - data[(position++)] = ((byte) (fileDatesInfoEntryId >> 16 & 0xFF)); - data[(position++)] = ((byte) (fileDatesInfoEntryId >> 8 & 0xFF)); - data[(position++)] = ((byte) (fileDatesInfoEntryId >> 0 & 0xFF)); - fileDatesInfoPosition = position; - data[(position++)] = ((byte) (fileDatesInfoEntryOffset >> 24 & 0xFF)); - data[(position++)] = ((byte) (fileDatesInfoEntryOffset >> 16 & 0xFF)); - data[(position++)] = ((byte) (fileDatesInfoEntryOffset >> 8 & 0xFF)); - data[(position++)] = ((byte) (fileDatesInfoEntryOffset >> 0 & 0xFF)); - data[(position++)] = ((byte) (fileDatesInfoEntryLength >> 24 & 0xFF)); - data[(position++)] = ((byte) (fileDatesInfoEntryLength >> 16 & 0xFF)); - data[(position++)] = ((byte) (fileDatesInfoEntryLength >> 8 & 0xFF)); - data[(position++)] = ((byte) (fileDatesInfoEntryLength >> 0 & 0xFF)); - } - - /* Resource fork entry header */ - int resourceForkPosition = 0; - if (hasResourceFork) { - int resourceForkEntryId = 2; - int resourceForkEntryOffset = 0; - int resourceFokrEntryLength = this.resourceFork.getLength(); - data[(position++)] = ((byte) (resourceForkEntryId >> 24 & 0xFF)); - data[(position++)] = ((byte) (resourceForkEntryId >> 16 & 0xFF)); - data[(position++)] = ((byte) (resourceForkEntryId >> 8 & 0xFF)); - data[(position++)] = ((byte) (resourceForkEntryId >> 0 & 0xFF)); - resourceForkPosition = position; - data[(position++)] = ((byte) (resourceForkEntryOffset >> 24 & 0xFF)); - data[(position++)] = ((byte) (resourceForkEntryOffset >> 16 & 0xFF)); - data[(position++)] = ((byte) (resourceForkEntryOffset >> 8 & 0xFF)); - data[(position++)] = ((byte) (resourceForkEntryOffset >> 0 & 0xFF)); - data[(position++)] = ((byte) (resourceFokrEntryLength >> 24 & 0xFF)); - data[(position++)] = ((byte) (resourceFokrEntryLength >> 16 & 0xFF)); - data[(position++)] = ((byte) (resourceFokrEntryLength >> 8 & 0xFF)); - data[(position++)] = ((byte) (resourceFokrEntryLength >> 0 & 0xFF)); - } - - /* Data fork entry header */ - int dataForkPosition = 0; - if ((hasDataFork) && (isAppleSingle)) { - int dataForkEntryId = 1; - int dataForkEntryOffset = 0; - int dataForkEntryLength = this.dataFork.getLength(); - data[(position++)] = ((byte) (dataForkEntryId >> 24 & 0xFF)); - data[(position++)] = ((byte) (dataForkEntryId >> 16 & 0xFF)); - data[(position++)] = ((byte) (dataForkEntryId >> 8 & 0xFF)); - data[(position++)] = ((byte) (dataForkEntryId >> 0 & 0xFF)); - dataForkPosition = position; - data[(position++)] = ((byte) (dataForkEntryOffset >> 24 & 0xFF)); - data[(position++)] = ((byte) (dataForkEntryOffset >> 16 & 0xFF)); - data[(position++)] = ((byte) (dataForkEntryOffset >> 8 & 0xFF)); - data[(position++)] = ((byte) (dataForkEntryOffset >> 0 & 0xFF)); - data[(position++)] = ((byte) (dataForkEntryLength >> 24 & 0xFF)); - data[(position++)] = ((byte) (dataForkEntryLength >> 16 & 0xFF)); - data[(position++)] = ((byte) (dataForkEntryLength >> 8 & 0xFF)); - data[(position++)] = ((byte) (dataForkEntryLength >> 0 & 0xFF)); - } - - /* Content for the entries */ - - /* Real name content */ - if (hasRealName) { - int realNamePositionCurrent = position; - position = realNamePosition; - data[(position++)] = ((byte) (realNamePositionCurrent >> 24 & 0xFF)); - data[(position++)] = ((byte) (realNamePositionCurrent >> 16 & 0xFF)); - data[(position++)] = ((byte) (realNamePositionCurrent >> 8 & 0xFF)); - data[(position++)] = ((byte) (realNamePositionCurrent >> 0 & 0xFF)); - position = realNamePositionCurrent; - byte[] realNameData = this.realName.getData(); - int realNameOffset = this.realName.getOffset(); - int realNameLength = this.realName.getLength(); - System.arraycopy(realNameData, realNameOffset, data, position, - realNameLength); - position += realNameLength; - } - - /* File dates info content */ - if (hasFileDatesInfo) { - int fileDatesInfoPositionCurrent = position; - position = fileDatesInfoPosition; - data[(position++)] = ((byte) (fileDatesInfoPositionCurrent >> 24 & 0xFF)); - data[(position++)] = ((byte) (fileDatesInfoPositionCurrent >> 16 & 0xFF)); - data[(position++)] = ((byte) (fileDatesInfoPositionCurrent >> 8 & 0xFF)); - data[(position++)] = ((byte) (fileDatesInfoPositionCurrent >> 0 & 0xFF)); - position = fileDatesInfoPositionCurrent; - data[(position++)] = ((byte) (this.fileDatesInfoEntry - .getCreateTime() >> 24 & 0xFF)); - data[(position++)] = ((byte) (this.fileDatesInfoEntry - .getCreateTime() >> 16 & 0xFF)); - data[(position++)] = ((byte) (this.fileDatesInfoEntry - .getCreateTime() >> 8 & 0xFF)); - data[(position++)] = ((byte) (this.fileDatesInfoEntry - .getCreateTime() >> 0 & 0xFF)); - data[(position++)] = ((byte) (this.fileDatesInfoEntry - .getModifyTime() >> 24 & 0xFF)); - data[(position++)] = ((byte) (this.fileDatesInfoEntry - .getModifyTime() >> 16 & 0xFF)); - data[(position++)] = ((byte) (this.fileDatesInfoEntry - .getModifyTime() >> 8 & 0xFF)); - data[(position++)] = ((byte) (this.fileDatesInfoEntry - .getModifyTime() >> 0 & 0xFF)); - data[(position++)] = ((byte) (this.fileDatesInfoEntry - .getBackupTime() >> 24 & 0xFF)); - data[(position++)] = ((byte) (this.fileDatesInfoEntry - .getBackupTime() >> 16 & 0xFF)); - data[(position++)] = ((byte) (this.fileDatesInfoEntry - .getBackupTime() >> 8 & 0xFF)); - data[(position++)] = ((byte) (this.fileDatesInfoEntry - .getBackupTime() >> 0 & 0xFF)); - data[(position++)] = ((byte) (this.fileDatesInfoEntry - .getAccessTime() >> 24 & 0xFF)); - data[(position++)] = ((byte) (this.fileDatesInfoEntry - .getAccessTime() >> 16 & 0xFF)); - data[(position++)] = ((byte) (this.fileDatesInfoEntry - .getAccessTime() >> 8 & 0xFF)); - data[(position++)] = ((byte) (this.fileDatesInfoEntry - .getAccessTime() >> 0 & 0xFF)); - } - - /* Resource fork content */ - if (hasResourceFork) { - int resourceForkPositionCurrent = position; - position = resourceForkPosition; - data[(position++)] = ((byte) (resourceForkPositionCurrent >> 24 & 0xFF)); - data[(position++)] = ((byte) (resourceForkPositionCurrent >> 16 & 0xFF)); - data[(position++)] = ((byte) (resourceForkPositionCurrent >> 8 & 0xFF)); - data[(position++)] = ((byte) (resourceForkPositionCurrent >> 0 & 0xFF)); - position = resourceForkPositionCurrent; - byte[] resourceForkData = this.resourceFork.getData(); - int resourceForkOffset = this.resourceFork.getOffset(); - int resourceForkLength = this.resourceFork.getLength(); - System.arraycopy(resourceForkData, resourceForkOffset, data, - position, resourceForkLength); - position += resourceForkLength; - } - - /* Data fork content */ - if ((hasDataFork) && (isAppleSingle)) { - int dataForkPosition2 = position; - position = dataForkPosition; - data[(position++)] = ((byte) (dataForkPosition2 >> 24 & 0xFF)); - data[(position++)] = ((byte) (dataForkPosition2 >> 16 & 0xFF)); - data[(position++)] = ((byte) (dataForkPosition2 >> 8 & 0xFF)); - data[(position++)] = ((byte) (dataForkPosition2 >> 0 & 0xFF)); - position = dataForkPosition2; - byte[] dataForkData = this.dataFork.getData(); - int dataForkOffset = this.dataFork.getOffset(); - int dataForkLength = this.dataFork.getLength(); - System.arraycopy(dataForkData, dataForkOffset, data, position, - dataForkLength); - position += dataForkLength; - } - - /* Create the Apple file data */ - this.fileData = new AppleFileData(data, 0, position); - } +/** + * Copyright 2012 Perforce Software Inc., All Rights Reserved. + */ +package com.perforce.p4java.io.apple; + +import com.perforce.p4java.exception.FileEncoderException; + +/** + * This class handles the combination of the data fork, resource fork and other + * entries into an AppleSingle/Double file. + *

+ * Note that if it is an AppleDouble, the data fork is a separate file external + * to this file. + */ +public class AppleFileEncoder extends AppleFile { + + /** + * Instantiates a new apple file decoder. + * + * @param fileFormat fileFormat + * @throws FileEncoderException on error + */ + public AppleFileEncoder(FileFormat fileFormat) throws FileEncoderException { + if (fileFormat == null) { + throw new FileEncoderException("Null file format passed to the AppleFileEncoder constructor."); + } + if (fileFormat == FileFormat.UNKNOWN) { + throw new FileEncoderException("Unknown file format passed to the AppleFileEncoder constructor."); + } + } + + /** + * Combine the data fork, resource fork and other entries into an + * AppleSingle/Double file. + * + * @throws FileEncoderException the file encoder exception + */ + @SuppressWarnings("unused") + public void combine() throws FileEncoderException { + + boolean isAppleSingle = (this.format == FileFormat.APPLE_SINGLE); + boolean isAppleDouble = (this.format == FileFormat.APPLE_DOUBLE); + + boolean hasDataFork = (this.dataFork != AppleFileData.EMPTY_FILE_DATA); + boolean hasResourceFork = (this.resourceFork != AppleFileData.EMPTY_FILE_DATA); + boolean hasRealName = (this.realName != AppleFileData.EMPTY_FILE_DATA); + boolean hasComment = (this.comment != AppleFileData.EMPTY_FILE_DATA); + boolean hasIconBW = (this.iconBW != AppleFileData.EMPTY_FILE_DATA); + boolean hasIconColor = (this.iconColor != AppleFileData.EMPTY_FILE_DATA); + boolean hasFileDatesInfo = (this.fileDatesInfoEntry != null); + boolean hasFinderInfo = (this.finderInfo != AppleFileData.EMPTY_FILE_DATA); + boolean hasMacintoshInfo = (this.macintoshInfo != AppleFileData.EMPTY_FILE_DATA); + boolean hasProDOSFileInfo = (this.proDOSFileInfo != AppleFileData.EMPTY_FILE_DATA); + boolean hasMsDOSFileInfo = (this.msDOSFileInfo != AppleFileData.EMPTY_FILE_DATA); + boolean hasShortName = (this.shortName != AppleFileData.EMPTY_FILE_DATA); + boolean hasAfpFileInfo = (this.afpFileInfo != AppleFileData.EMPTY_FILE_DATA); + boolean hasDirectoryID = (this.directoryID != AppleFileData.EMPTY_FILE_DATA); + + this.fileData = AppleFileData.EMPTY_FILE_DATA; + + int length = 90 + this.realName.getLength() + + this.resourceFork.getLength(); + + /* AppleSingle includes the data fork */ + if (isAppleSingle) { + length += this.dataFork.getLength(); + } + + byte[] data = new byte[length]; + int position = 0; + + /* Magic number for AppleSingle or AppleDouble */ + if (isAppleDouble) { + data[(position++)] = 0; + data[(position++)] = 5; + data[(position++)] = 22; + data[(position++)] = 0; + } else { + data[(position++)] = 0; + data[(position++)] = 5; + data[(position++)] = 22; + data[(position++)] = 7; + } + + /* Version number */ + data[(position++)] = 0; + data[(position++)] = 2; + data[(position++)] = 0; + data[(position++)] = 0; + + /* Filler */ + + for (int k = 0; k < 16; k++) { + data[(position++)] = 0; + } + + /* Number of entries */ + this.numEntries = 0; + if (hasRealName) { + this.numEntries += 1; + } + if (hasFileDatesInfo) { + this.numEntries += 1; + } + if (hasResourceFork) { + this.numEntries += 1; + } + if ((hasDataFork) && (isAppleSingle)) { + this.numEntries += 1; + } + data[(position++)] = ((byte) (this.numEntries >> 8 & 0xFF)); + data[(position++)] = ((byte) (this.numEntries & 0xFF)); + + /* Header information for the entries */ + + /* Real name entry header */ + int realNamePosition = 0; + if (hasRealName) { + int realNameEntryId = 3; + int realNameEntryOffset = 0; + int realNameEntryLength = this.realName.getLength(); + data[(position++)] = ((byte) (realNameEntryId >> 24 & 0xFF)); + data[(position++)] = ((byte) (realNameEntryId >> 16 & 0xFF)); + data[(position++)] = ((byte) (realNameEntryId >> 8 & 0xFF)); + data[(position++)] = ((byte) (realNameEntryId >> 0 & 0xFF)); + realNamePosition = position; + data[(position++)] = ((byte) (realNameEntryOffset >> 24 & 0xFF)); + data[(position++)] = ((byte) (realNameEntryOffset >> 16 & 0xFF)); + data[(position++)] = ((byte) (realNameEntryOffset >> 8 & 0xFF)); + data[(position++)] = ((byte) (realNameEntryOffset >> 0 & 0xFF)); + data[(position++)] = ((byte) (realNameEntryLength >> 24 & 0xFF)); + data[(position++)] = ((byte) (realNameEntryLength >> 16 & 0xFF)); + data[(position++)] = ((byte) (realNameEntryLength >> 8 & 0xFF)); + data[(position++)] = ((byte) (realNameEntryLength >> 0 & 0xFF)); + } + + /* File dates info entry header */ + int fileDatesInfoPosition = 0; + if (hasFileDatesInfo) { + int fileDatesInfoEntryId = 8; + int fileDatesInfoEntryOffset = 0; + int fileDatesInfoEntryLength = 16; + data[(position++)] = ((byte) (fileDatesInfoEntryId >> 24 & 0xFF)); + data[(position++)] = ((byte) (fileDatesInfoEntryId >> 16 & 0xFF)); + data[(position++)] = ((byte) (fileDatesInfoEntryId >> 8 & 0xFF)); + data[(position++)] = ((byte) (fileDatesInfoEntryId >> 0 & 0xFF)); + fileDatesInfoPosition = position; + data[(position++)] = ((byte) (fileDatesInfoEntryOffset >> 24 & 0xFF)); + data[(position++)] = ((byte) (fileDatesInfoEntryOffset >> 16 & 0xFF)); + data[(position++)] = ((byte) (fileDatesInfoEntryOffset >> 8 & 0xFF)); + data[(position++)] = ((byte) (fileDatesInfoEntryOffset >> 0 & 0xFF)); + data[(position++)] = ((byte) (fileDatesInfoEntryLength >> 24 & 0xFF)); + data[(position++)] = ((byte) (fileDatesInfoEntryLength >> 16 & 0xFF)); + data[(position++)] = ((byte) (fileDatesInfoEntryLength >> 8 & 0xFF)); + data[(position++)] = ((byte) (fileDatesInfoEntryLength >> 0 & 0xFF)); + } + + /* Resource fork entry header */ + int resourceForkPosition = 0; + if (hasResourceFork) { + int resourceForkEntryId = 2; + int resourceForkEntryOffset = 0; + int resourceFokrEntryLength = this.resourceFork.getLength(); + data[(position++)] = ((byte) (resourceForkEntryId >> 24 & 0xFF)); + data[(position++)] = ((byte) (resourceForkEntryId >> 16 & 0xFF)); + data[(position++)] = ((byte) (resourceForkEntryId >> 8 & 0xFF)); + data[(position++)] = ((byte) (resourceForkEntryId >> 0 & 0xFF)); + resourceForkPosition = position; + data[(position++)] = ((byte) (resourceForkEntryOffset >> 24 & 0xFF)); + data[(position++)] = ((byte) (resourceForkEntryOffset >> 16 & 0xFF)); + data[(position++)] = ((byte) (resourceForkEntryOffset >> 8 & 0xFF)); + data[(position++)] = ((byte) (resourceForkEntryOffset >> 0 & 0xFF)); + data[(position++)] = ((byte) (resourceFokrEntryLength >> 24 & 0xFF)); + data[(position++)] = ((byte) (resourceFokrEntryLength >> 16 & 0xFF)); + data[(position++)] = ((byte) (resourceFokrEntryLength >> 8 & 0xFF)); + data[(position++)] = ((byte) (resourceFokrEntryLength >> 0 & 0xFF)); + } + + /* Data fork entry header */ + int dataForkPosition = 0; + if ((hasDataFork) && (isAppleSingle)) { + int dataForkEntryId = 1; + int dataForkEntryOffset = 0; + int dataForkEntryLength = this.dataFork.getLength(); + data[(position++)] = ((byte) (dataForkEntryId >> 24 & 0xFF)); + data[(position++)] = ((byte) (dataForkEntryId >> 16 & 0xFF)); + data[(position++)] = ((byte) (dataForkEntryId >> 8 & 0xFF)); + data[(position++)] = ((byte) (dataForkEntryId >> 0 & 0xFF)); + dataForkPosition = position; + data[(position++)] = ((byte) (dataForkEntryOffset >> 24 & 0xFF)); + data[(position++)] = ((byte) (dataForkEntryOffset >> 16 & 0xFF)); + data[(position++)] = ((byte) (dataForkEntryOffset >> 8 & 0xFF)); + data[(position++)] = ((byte) (dataForkEntryOffset >> 0 & 0xFF)); + data[(position++)] = ((byte) (dataForkEntryLength >> 24 & 0xFF)); + data[(position++)] = ((byte) (dataForkEntryLength >> 16 & 0xFF)); + data[(position++)] = ((byte) (dataForkEntryLength >> 8 & 0xFF)); + data[(position++)] = ((byte) (dataForkEntryLength >> 0 & 0xFF)); + } + + /* Content for the entries */ + + /* Real name content */ + if (hasRealName) { + int realNamePositionCurrent = position; + position = realNamePosition; + data[(position++)] = ((byte) (realNamePositionCurrent >> 24 & 0xFF)); + data[(position++)] = ((byte) (realNamePositionCurrent >> 16 & 0xFF)); + data[(position++)] = ((byte) (realNamePositionCurrent >> 8 & 0xFF)); + data[(position++)] = ((byte) (realNamePositionCurrent >> 0 & 0xFF)); + position = realNamePositionCurrent; + byte[] realNameData = this.realName.getData(); + int realNameOffset = this.realName.getOffset(); + int realNameLength = this.realName.getLength(); + System.arraycopy(realNameData, realNameOffset, data, position, + realNameLength); + position += realNameLength; + } + + /* File dates info content */ + if (hasFileDatesInfo) { + int fileDatesInfoPositionCurrent = position; + position = fileDatesInfoPosition; + data[(position++)] = ((byte) (fileDatesInfoPositionCurrent >> 24 & 0xFF)); + data[(position++)] = ((byte) (fileDatesInfoPositionCurrent >> 16 & 0xFF)); + data[(position++)] = ((byte) (fileDatesInfoPositionCurrent >> 8 & 0xFF)); + data[(position++)] = ((byte) (fileDatesInfoPositionCurrent >> 0 & 0xFF)); + position = fileDatesInfoPositionCurrent; + data[(position++)] = ((byte) (this.fileDatesInfoEntry + .getCreateTime() >> 24 & 0xFF)); + data[(position++)] = ((byte) (this.fileDatesInfoEntry + .getCreateTime() >> 16 & 0xFF)); + data[(position++)] = ((byte) (this.fileDatesInfoEntry + .getCreateTime() >> 8 & 0xFF)); + data[(position++)] = ((byte) (this.fileDatesInfoEntry + .getCreateTime() >> 0 & 0xFF)); + data[(position++)] = ((byte) (this.fileDatesInfoEntry + .getModifyTime() >> 24 & 0xFF)); + data[(position++)] = ((byte) (this.fileDatesInfoEntry + .getModifyTime() >> 16 & 0xFF)); + data[(position++)] = ((byte) (this.fileDatesInfoEntry + .getModifyTime() >> 8 & 0xFF)); + data[(position++)] = ((byte) (this.fileDatesInfoEntry + .getModifyTime() >> 0 & 0xFF)); + data[(position++)] = ((byte) (this.fileDatesInfoEntry + .getBackupTime() >> 24 & 0xFF)); + data[(position++)] = ((byte) (this.fileDatesInfoEntry + .getBackupTime() >> 16 & 0xFF)); + data[(position++)] = ((byte) (this.fileDatesInfoEntry + .getBackupTime() >> 8 & 0xFF)); + data[(position++)] = ((byte) (this.fileDatesInfoEntry + .getBackupTime() >> 0 & 0xFF)); + data[(position++)] = ((byte) (this.fileDatesInfoEntry + .getAccessTime() >> 24 & 0xFF)); + data[(position++)] = ((byte) (this.fileDatesInfoEntry + .getAccessTime() >> 16 & 0xFF)); + data[(position++)] = ((byte) (this.fileDatesInfoEntry + .getAccessTime() >> 8 & 0xFF)); + data[(position++)] = ((byte) (this.fileDatesInfoEntry + .getAccessTime() >> 0 & 0xFF)); + } + + /* Resource fork content */ + if (hasResourceFork) { + int resourceForkPositionCurrent = position; + position = resourceForkPosition; + data[(position++)] = ((byte) (resourceForkPositionCurrent >> 24 & 0xFF)); + data[(position++)] = ((byte) (resourceForkPositionCurrent >> 16 & 0xFF)); + data[(position++)] = ((byte) (resourceForkPositionCurrent >> 8 & 0xFF)); + data[(position++)] = ((byte) (resourceForkPositionCurrent >> 0 & 0xFF)); + position = resourceForkPositionCurrent; + byte[] resourceForkData = this.resourceFork.getData(); + int resourceForkOffset = this.resourceFork.getOffset(); + int resourceForkLength = this.resourceFork.getLength(); + System.arraycopy(resourceForkData, resourceForkOffset, data, + position, resourceForkLength); + position += resourceForkLength; + } + + /* Data fork content */ + if ((hasDataFork) && (isAppleSingle)) { + int dataForkPosition2 = position; + position = dataForkPosition; + data[(position++)] = ((byte) (dataForkPosition2 >> 24 & 0xFF)); + data[(position++)] = ((byte) (dataForkPosition2 >> 16 & 0xFF)); + data[(position++)] = ((byte) (dataForkPosition2 >> 8 & 0xFF)); + data[(position++)] = ((byte) (dataForkPosition2 >> 0 & 0xFF)); + position = dataForkPosition2; + byte[] dataForkData = this.dataFork.getData(); + int dataForkOffset = this.dataFork.getOffset(); + int dataForkLength = this.dataFork.getLength(); + System.arraycopy(dataForkData, dataForkOffset, data, position, + dataForkLength); + position += dataForkLength; + } + + /* Create the Apple file data */ + this.fileData = new AppleFileData(data, 0, position); + } } \ No newline at end of file diff --git a/src/main/java/com/perforce/p4java/option/server/GetFileContentsOptions.java b/src/main/java/com/perforce/p4java/option/server/GetFileContentsOptions.java index c95c252..80fcf1a 100644 --- a/src/main/java/com/perforce/p4java/option/server/GetFileContentsOptions.java +++ b/src/main/java/com/perforce/p4java/option/server/GetFileContentsOptions.java @@ -3,6 +3,7 @@ */ package com.perforce.p4java.option.server; +import com.perforce.p4java.exception.ConnectionException; import com.perforce.p4java.exception.OptionsException; import com.perforce.p4java.option.Options; import com.perforce.p4java.server.IServer; @@ -17,10 +18,15 @@ public class GetFileContentsOptions extends Options { /** - * Options: -a, -q + * Options: -a, -q, --offset, --size */ public static final String OPTIONS_SPECS = "b:a b:q"; + /** + * Options: --offset, --size + */ + public static final String OPTION_SPEC_NEW = "l:-offset l:-size"; + /** * If true, get the contents of all revisions within the specific range, rather * than just the highest revision in the range. Corresponds to -a. @@ -38,13 +44,22 @@ public class GetFileContentsOptions extends Options { * (Parameters.processParameters(...)). By default the filespecs passed to * IOptionsServer.getFileContents() would get revisions appended to them * during parameter processing.

- * * Note that this is not a standard option for this command. It is merely a * convenience flag to tell the parameter processor not to include revisions * with the filespecs. */ protected boolean dontAnnotateFiles = false; + /** + * Skip the specified number of bytes and only print what follows. + */ + protected long offset = 0L; + + /** + * Print the specified number of bytes from the offset. + */ + protected long size = 0L; + /** * Default constructor -- sets all fields to false. */ @@ -91,7 +106,20 @@ public GetFileContentsOptions(boolean allrevs, boolean noHeaderLine) { * @see com.perforce.p4java.option.Options#processOptions(com.perforce.p4java.server.IServer) */ public List processOptions(IServer server) throws OptionsException { - this.optionList = this.processFields(OPTIONS_SPECS, this.isAllrevs(), this.isNoHeaderLine()); + int serverVersion = 0; + try { + serverVersion = server.getServerVersion(); + } catch (ConnectionException e) { + throw new OptionsException("Can not connect to server.", e); + } + if (serverVersion >= 20221) { + this.optionList = this.processFields(OPTIONS_SPECS + " " + OPTION_SPEC_NEW, + this.isAllrevs(), this.isNoHeaderLine(), + this.offset, this.size); + + } else { + this.optionList = this.processFields(OPTIONS_SPECS, this.isAllrevs(), this.isNoHeaderLine()); + } return this.optionList; } @@ -121,4 +149,20 @@ public GetFileContentsOptions setDontAnnotateFiles(boolean dontAnnotateFiles) { this.dontAnnotateFiles = dontAnnotateFiles; return this; } + + public long getOffset() { + return offset; + } + + public void setOffset(long offset) { + this.offset = offset; + } + + public long getSize() { + return size; + } + + public void setSize(long size) { + this.size = size; + } } diff --git a/src/main/java/com/perforce/p4java/option/server/OptionsHelper.java b/src/main/java/com/perforce/p4java/option/server/OptionsHelper.java index 3ec815a..0ebf991 100644 --- a/src/main/java/com/perforce/p4java/option/server/OptionsHelper.java +++ b/src/main/java/com/perforce/p4java/option/server/OptionsHelper.java @@ -17,188 +17,203 @@ * @since 8/09/2016 */ public class OptionsHelper { - private OptionsHelper() { /* util */ } - - /** - * String used to prefix options for the server. This is pretty fundamental; - * don't change this unless you really know what you're doing.... - */ - public static final String OPTPFX = "-"; - - /** - * Apply an optional rule to a boolean option value. This method is always - * called by the default implementation of Options.processOptions to process - * boolean values; you should override this if you need your own rules processing.

- * - * There are currently no rules recognised or implemented in this method. - * - * @param ruleName rule name string from the options spec string. If null, no rule was - * specified. - * @param serverOptStr the flag string to be sent to the Perforce server prefixing this value - * @param value the boolean value itself. - * @return processed value or null if the rules resulted in nothing to be sent to the Perforce - * server. - * @throws OptionsException if any errors occurred during options processing. - */ - public static String applyRule(String ruleName, String serverOptStr, boolean value) throws OptionsException { - throwOptionsExceptionIfConditionFails(isNotBlank(serverOptStr), "Null or Empty server options spec"); - - if (isBlank(ruleName)) { - if (value) { - return OPTPFX + serverOptStr; - } - } else { - throwOptionsException("Unrecognized option rule name in options parser: '%s'", ruleName); - } - - return EMPTY; - } - - /** - * Apply an optional rule to an integer option value. This method is always - * called by the default implementation of Options.processOptions to process - * integer values; you should override this if you need your own rules processing.

- * - * This version of applyRules implements the rules specified below: - *

-   * "gtz": don't return anything unless the value is > 0; typically used for
-   * 		things like maxUsers or maxRows.
-   * "cl": ignore negative values; convert 0 to the string "default". Typically
-   * 		used for changelists.
-   * "clz": ignore non-positive values; typically used for changelists where we
-   * 		let the server infer "default" for IChangelist.DEFAULT rather than
-   * 		spelling it out.
-   * "dcn": implements the -dc[n] rule for diff contexts, i.e. if the int value is
-   * 		zero, emit the flag alone; if it's positive, emit the flag with the int
-   * 		value attached; if it's negative, don't emit anything.
-   * 
- * If the passed-in ruleName is non-null and not recognized, the behaviour - * is the same as if a null rule name was passed in. - * - * @param ruleName rule name string from the options spec string. If null, no rule was - * specified. - * @param serverOptStr the flag string to be sent to the Perforce server prefixing this value - * @param value the integer value itself. - * @return processed value or null if the rules resulted in nothing to be sent to the Perforce - * server. - * @throws OptionsException if any errors occurred during options processing. - */ - public static String applyRule(String ruleName, String serverOptStr, int value) throws OptionsException { - throwOptionsExceptionIfConditionFails(isNotBlank(serverOptStr), "Null or Empty server options spec"); - - if (isBlank(ruleName)) { - return OPTPFX + serverOptStr + value; - } else { - if ("gtz".equals(ruleName) || "clz".equals(ruleName)) { - if (value > 0) { - return OPTPFX + serverOptStr + value; - } - } else if ("cl".equals(ruleName)) { - if (value >= 0) { - return OPTPFX + serverOptStr + (value == IChangelist.DEFAULT ? "default" : value); - } - } else if ("dcn".equals(ruleName)) { - if (value > 0) { - return OPTPFX + serverOptStr + value; - } else if (value == 0) { - return OPTPFX + serverOptStr; - } - } else { - throwOptionsException("Unrecognized option rule name in options parser: '%s'", ruleName); - } - } - - return EMPTY; - } - - /** - * Apply an optional rule to a long option value. This method is always - * called by the default implementation of Options.processOptions to process - * long values; you should override this if you need your own rules processing.

- * - * This version of applyRules implements the rules specified below: - *

-   * "gtz": don't return anything unless the value is > 0.
-   * "gez": don't return anything unless the value is >= 0.
-   * 
- * If the passed-in ruleName is non-null and not recognized, the behaviour - * is the same as if a null rule name was passed in. - * - * @param ruleName rule name string from the options spec string. If null, no rule was - * specified. - * @param serverOptStr the flag string to be sent to the Perforce server prefixing this value - * @param value the long value itself. - * @return processed value or null if the rules resulted in nothing to be sent to the Perforce - * server. - * @throws OptionsException if any errors occurred during options processing. - */ - public static String applyRule(String ruleName, String serverOptStr, long value) throws OptionsException { - throwOptionsExceptionIfConditionFails(isNotBlank(serverOptStr), "Null or Empty server options spec"); - - if (isBlank(ruleName)) { - return OPTPFX + serverOptStr + value; - } else { - if ("gtz".equals(ruleName)) { - if (value > 0) { - return OPTPFX + serverOptStr + value; - } - } else if ("gez".equals(ruleName)) { - if (value >= 0) { - return OPTPFX + serverOptStr + value; - } - } else { - throwOptionsException("Unrecognized option rule name in options parser: '%s'", ruleName); - } - } - - return EMPTY; - } - - /** - * Apply an optional rule to a string option value. This method is always - * called by the default implementation of Options.processOptions to process - * string values; you should override this if you need your own rules processing.

- * - * There are currently no rules recognised or implemented in this method. - * - * @param ruleName rule name string from the options spec string. If null, no rule was - * specified. - * @param serverOptStr the flag string to be sent to the Perforce server prefixing this value - * @param value the string value itself; may be null. - * @return processed value or null if the rules resulted in nothing to be sent to the Perforce - * server. - * @throws OptionsException if any errors occurred during options processing. - */ - public static String applyRule(String ruleName, String serverOptStr, String value) throws OptionsException { - throwOptionsExceptionIfConditionFails(isNotBlank(serverOptStr), "Null or Empty server options spec"); - - if (isBlank(ruleName)) { - if (isNotBlank(value)) { - return OPTPFX + serverOptStr + value; - } - } else { - throwOptionsException("Unrecognized option rule name in options parser: '%s'", ruleName); - } - - return EMPTY; - } - - public static boolean objectToBoolean(Object optValue) throws OptionsException { - throwOptionsExceptionIfConditionFails(nonNull(optValue), ".option value can not be NULL"); - if (optValue instanceof String) { - String value = String.valueOf(optValue); - if ("true".equalsIgnoreCase(value)) { - return true; - } else if ("false".equalsIgnoreCase(value)) { - return false; - } else { - throwOptionsException("Invalid boolean type options value: %s. \nBoolean type options value is only 'true' or 'false' (case insensitive).", Objects.toString(optValue)); - } - } - - if (optValue instanceof Boolean) { - return (boolean) optValue; - } - throw new OptionsException("Invalid boolean type options value: " + Objects.toString(optValue)); - } + private OptionsHelper() { /* util */ } + + /** + * String used to prefix options for the server. This is pretty fundamental; + * don't change this unless you really know what you're doing.... + */ + public static final String OPTPFX = "-"; + + /** + * Apply an optional rule to a boolean option value. This method is always + * called by the default implementation of Options.processOptions to process + * boolean values; you should override this if you need your own rules processing.

+ * + * There are currently no rules recognised or implemented in this method. + * + * @param ruleName rule name string from the options spec string. If null, no rule was + * specified. + * @param serverOptStr the flag string to be sent to the Perforce server prefixing this value + * @param value the boolean value itself. + * @return processed value or null if the rules resulted in nothing to be sent to the Perforce + * server. + * @throws OptionsException if any errors occurred during options processing. + */ + public static String applyRule(String ruleName, String serverOptStr, boolean value) throws OptionsException { + throwOptionsExceptionIfConditionFails(isNotBlank(serverOptStr), "Null or Empty server options spec"); + + if (isBlank(ruleName)) { + if (value) { + return OPTPFX + serverOptStr; + } + } else { + throwOptionsException("Unrecognized option rule name in options parser: '%s'", ruleName); + } + + return EMPTY; + } + + /** + * Apply an optional rule to an integer option value. This method is always + * called by the default implementation of Options.processOptions to process + * integer values; you should override this if you need your own rules processing.

+ * + * This version of applyRules implements the rules specified below: + *

+	 * "gtz": don't return anything unless the value is > 0; typically used for
+	 * 		things like maxUsers or maxRows.
+	 * "cl": ignore negative values; convert 0 to the string "default". Typically
+	 * 		used for changelists.
+	 * "clz": ignore non-positive values; typically used for changelists where we
+	 * 		let the server infer "default" for IChangelist.DEFAULT rather than
+	 * 		spelling it out.
+	 * "dcn": implements the -dc[n] rule for diff contexts, i.e. if the int value is
+	 * 		zero, emit the flag alone; if it's positive, emit the flag with the int
+	 * 		value attached; if it's negative, don't emit anything.
+	 * 
+ * If the passed-in ruleName is non-null and not recognized, the behaviour + * is the same as if a null rule name was passed in. + * + * @param ruleName rule name string from the options spec string. If null, no rule was + * specified. + * @param serverOptStr the flag string to be sent to the Perforce server prefixing this value + * If the flag start with "-" it will be passed as "--flag=value". Eg: The flag "-offset" will + * be passed as "--offset=value" + * @param value the integer value itself. + * @return processed value or null if the rules resulted in nothing to be sent to the Perforce + * server. + * @throws OptionsException if any errors occurred during options processing. + */ + public static String applyRule(String ruleName, String serverOptStr, int value) throws OptionsException { + throwOptionsExceptionIfConditionFails(isNotBlank(serverOptStr), "Null or Empty server options spec"); + if (serverOptStr.startsWith("-")) { + serverOptStr = serverOptStr + "="; + } + if (isBlank(ruleName)) { + return OPTPFX + serverOptStr + value; + } else { + if ("gtz".equals(ruleName) || "clz".equals(ruleName)) { + if (value > 0) { + return OPTPFX + serverOptStr + value; + } + } else if ("cl".equals(ruleName)) { + if (value >= 0) { + return OPTPFX + serverOptStr + (value == IChangelist.DEFAULT ? "default" : value); + } + } else if ("dcn".equals(ruleName)) { + if (value > 0) { + return OPTPFX + serverOptStr + value; + } else if (value == 0) { + return OPTPFX + serverOptStr; + } + } else { + throwOptionsException("Unrecognized option rule name in options parser: '%s'", ruleName); + } + } + + return EMPTY; + } + + /** + * Apply an optional rule to a long option value. This method is always + * called by the default implementation of Options.processOptions to process + * long values; you should override this if you need your own rules processing.

+ * + * This version of applyRules implements the rules specified below: + *

+	 * "gtz": don't return anything unless the value is > 0.
+	 * "gez": don't return anything unless the value is >= 0.
+	 * 
+ * If the passed-in ruleName is non-null and not recognized, the behaviour + * is the same as if a null rule name was passed in. + * + * @param ruleName rule name string from the options spec string. If null, no rule was + * specified. + * @param serverOptStr the flag string to be sent to the Perforce server prefixing this value + * If the flag start with "-" it will be passed as "--flag=value". Eg: The flag "-offset" will + * be passed as "--offset=value" + * @param value the long value itself. + * @return processed value or null if the rules resulted in nothing to be sent to the Perforce + * server. + * @throws OptionsException if any errors occurred during options processing. + */ + public static String applyRule(String ruleName, String serverOptStr, long value) throws OptionsException { + throwOptionsExceptionIfConditionFails(isNotBlank(serverOptStr), "Null or Empty server options spec"); + + if (serverOptStr.startsWith("-")) { + serverOptStr = serverOptStr + "="; + } + if (isBlank(ruleName)) { + return OPTPFX + serverOptStr + value; + } else { + if ("gtz".equals(ruleName)) { + if (value > 0) { + return OPTPFX + serverOptStr + value; + } + } else if ("gez".equals(ruleName)) { + if (value >= 0) { + return OPTPFX + serverOptStr + value; + } + } else { + throwOptionsException("Unrecognized option rule name in options parser: '%s'", ruleName); + } + } + + return EMPTY; + } + + /** + * Apply an optional rule to a string option value. This method is always + * called by the default implementation of Options.processOptions to process + * string values; you should override this if you need your own rules processing.

+ *

+ * There are currently no rules recognised or implemented in this method. + * + * @param ruleName rule name string from the options spec string. If null, no rule was + * specified. + * @param serverOptStr the flag string to be sent to the Perforce server prefixing this value + * If the flag start with "-" it will be passed as "--flag=value". Eg: The flag "-offset" will + * be passed as "--offset=value" + * @param value the string value itself; may be null. + * @return processed value or null if the rules resulted in nothing to be sent to the Perforce + * server. + * @throws OptionsException if any errors occurred during options processing. + */ + public static String applyRule(String ruleName, String serverOptStr, String value) throws OptionsException { + throwOptionsExceptionIfConditionFails(isNotBlank(serverOptStr), "Null or Empty server options spec"); + if (serverOptStr.startsWith("-")) { + serverOptStr = serverOptStr + "="; + } + + if (isBlank(ruleName)) { + if (isNotBlank(value)) { + return OPTPFX + serverOptStr + value; + } + } else { + throwOptionsException("Unrecognized option rule name in options parser: '%s'", ruleName); + } + + return EMPTY; + } + + public static boolean objectToBoolean(Object optValue) throws OptionsException { + throwOptionsExceptionIfConditionFails(nonNull(optValue), ".option value can not be NULL"); + + if (optValue instanceof String) { + String value = String.valueOf(optValue); + if ("true".equalsIgnoreCase(value)) { + return true; + } else if ("false".equalsIgnoreCase(value)) { + return false; + } else { + throwOptionsException("Invalid boolean type options value: %s. \nBoolean type options value is only 'true' or 'false' (case insensitive).", Objects.toString(optValue)); + } + } + + if (optValue instanceof Boolean) { + return (boolean) optValue; + } + throw new OptionsException("Invalid boolean type options value: " + Objects.toString(optValue)); + } }