• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements.  See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership.  The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License.  You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing,
13  * software distributed under the License is distributed on an
14  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15  * KIND, either express or implied.  See the License for the
16  * specific language governing permissions and limitations
17  * under the License.
18  */
19 package org.apache.commons.compress.archivers.zip;
20 
21 
22 import java.util.zip.ZipException;
23 
24 /**
25  * An extra field who's sole purpose is to align and pad the local file header
26  * so that the entry's data starts at a certain position.
27  *
28  * <p>The padding content of the padding is ignored and not retained
29  * when reading a padding field.</p>
30  *
31  * <p>This enables Commons Compress to create "aligned" archives
32  * similar to Android's zipalign command line tool.</p>
33  *
34  * @since 1.14
35  * @see "https://developer.android.com/studio/command-line/zipalign.html"
36  * @see ZipArchiveEntry#setAlignment
37  */
38 public class ResourceAlignmentExtraField implements ZipExtraField {
39 
40     /**
41      * Extra field id used for storing alignment and padding.
42      */
43     public static final ZipShort ID = new ZipShort(0xa11e);
44 
45     public static final int BASE_SIZE = 2;
46 
47     private static final int ALLOW_METHOD_MESSAGE_CHANGE_FLAG = 0x8000;
48 
49     private short alignment;
50 
51     private boolean allowMethodChange;
52 
53     private int padding = 0;
54 
ResourceAlignmentExtraField()55     public ResourceAlignmentExtraField() {
56     }
57 
ResourceAlignmentExtraField(int alignment)58     public ResourceAlignmentExtraField(int alignment) {
59         this(alignment, false);
60     }
61 
ResourceAlignmentExtraField(int alignment, boolean allowMethodChange)62     public ResourceAlignmentExtraField(int alignment, boolean allowMethodChange) {
63         this(alignment, allowMethodChange, 0);
64     }
65 
ResourceAlignmentExtraField(int alignment, boolean allowMethodChange, int padding)66     public ResourceAlignmentExtraField(int alignment, boolean allowMethodChange, int padding) {
67         if (alignment < 0 || alignment > 0x7fff) {
68             throw new IllegalArgumentException("Alignment must be between 0 and 0x7fff, was: " + alignment);
69         }
70         this.alignment = (short) alignment;
71         this.allowMethodChange = allowMethodChange;
72         this.padding = padding;
73     }
74 
75     /**
76      * Gets requested alignment.
77      *
78      * @return
79      *      requested alignment.
80      */
getAlignment()81     public short getAlignment() {
82         return alignment;
83     }
84 
85     /**
86      * Indicates whether method change is allowed when re-compressing the zip file.
87      *
88      * @return
89      *      true if method change is allowed, false otherwise.
90      */
allowMethodChange()91     public boolean allowMethodChange() {
92         return allowMethodChange;
93     }
94 
95     @Override
getHeaderId()96     public ZipShort getHeaderId() {
97         return ID;
98     }
99 
100     @Override
getLocalFileDataLength()101     public ZipShort getLocalFileDataLength() {
102         return new ZipShort(BASE_SIZE + padding);
103     }
104 
105     @Override
getCentralDirectoryLength()106     public ZipShort getCentralDirectoryLength() {
107         return new ZipShort(BASE_SIZE);
108     }
109 
110     @Override
getLocalFileDataData()111     public byte[] getLocalFileDataData() {
112         byte[] content = new byte[BASE_SIZE + padding];
113         ZipShort.putShort(alignment | (allowMethodChange ? ALLOW_METHOD_MESSAGE_CHANGE_FLAG : 0),
114                           content, 0);
115         return content;
116     }
117 
118     @Override
getCentralDirectoryData()119     public byte[] getCentralDirectoryData() {
120         return ZipShort.getBytes(alignment | (allowMethodChange ? ALLOW_METHOD_MESSAGE_CHANGE_FLAG : 0));
121     }
122 
123     @Override
parseFromLocalFileData(byte[] buffer, int offset, int length)124     public void parseFromLocalFileData(byte[] buffer, int offset, int length) throws ZipException {
125         parseFromCentralDirectoryData(buffer, offset, length);
126         this.padding = length - BASE_SIZE;
127     }
128 
129     @Override
parseFromCentralDirectoryData(byte[] buffer, int offset, int length)130     public void parseFromCentralDirectoryData(byte[] buffer, int offset, int length) throws ZipException {
131         if (length < BASE_SIZE) {
132             throw new ZipException("Too short content for ResourceAlignmentExtraField (0xa11e): " + length);
133         }
134         int alignmentValue = ZipShort.getValue(buffer, offset);
135         this.alignment = (short) (alignmentValue & (ALLOW_METHOD_MESSAGE_CHANGE_FLAG - 1));
136         this.allowMethodChange = (alignmentValue & ALLOW_METHOD_MESSAGE_CHANGE_FLAG) != 0;
137     }
138 }
139