You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

XSSFHyperlink.java 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430
  1. /* ====================================================================
  2. Licensed to the Apache Software Foundation (ASF) under one or more
  3. contributor license agreements. See the NOTICE file distributed with
  4. this work for additional information regarding copyright ownership.
  5. The ASF licenses this file to You under the Apache License, Version 2.0
  6. (the "License"); you may not use this file except in compliance with
  7. the License. You may obtain a copy of the License at
  8. http://www.apache.org/licenses/LICENSE-2.0
  9. Unless required by applicable law or agreed to in writing, software
  10. distributed under the License is distributed on an "AS IS" BASIS,
  11. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. See the License for the specific language governing permissions and
  13. limitations under the License.
  14. ==================================================================== */
  15. package org.apache.poi.xssf.usermodel;
  16. import java.net.URI;
  17. import java.net.URISyntaxException;
  18. import org.apache.poi.common.Duplicatable;
  19. import org.apache.poi.common.usermodel.HyperlinkType;
  20. import org.apache.poi.openxml4j.opc.PackagePart;
  21. import org.apache.poi.openxml4j.opc.PackageRelationship;
  22. import org.apache.poi.ss.SpreadsheetVersion;
  23. import org.apache.poi.ss.usermodel.Hyperlink;
  24. import org.apache.poi.ss.util.AreaReference;
  25. import org.apache.poi.ss.util.CellReference;
  26. import org.apache.poi.util.Internal;
  27. import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTHyperlink;
  28. /**
  29. * XSSF Implementation of a Hyperlink.
  30. * Note - unlike with HSSF, many kinds of hyperlink
  31. * are largely stored as relations of the sheet
  32. */
  33. public class XSSFHyperlink implements Hyperlink, Duplicatable {
  34. final private HyperlinkType _type;
  35. final private PackageRelationship _externalRel;
  36. final private CTHyperlink _ctHyperlink; //contains a reference to the cell where the hyperlink is anchored, getRef()
  37. private String _location; //what the hyperlink refers to
  38. /**
  39. * Create a new XSSFHyperlink. This method is protected to be used only by
  40. * {@link XSSFCreationHelper#createHyperlink(HyperlinkType)}.
  41. *
  42. * @param type - the type of hyperlink to create
  43. */
  44. protected XSSFHyperlink(HyperlinkType type) {
  45. _type = type;
  46. _ctHyperlink = CTHyperlink.Factory.newInstance();
  47. _externalRel = null;
  48. }
  49. /**
  50. * Create a XSSFHyperlink and initialize it from the supplied CTHyperlink bean and package relationship
  51. *
  52. * @param ctHyperlink the xml bean containing xml properties
  53. * @param hyperlinkRel the relationship in the underlying OPC package which stores the actual link's address
  54. * @throws IllegalStateException if ctHyperlink Id is null
  55. */
  56. protected XSSFHyperlink(CTHyperlink ctHyperlink, PackageRelationship hyperlinkRel) {
  57. _ctHyperlink = ctHyperlink;
  58. _externalRel = hyperlinkRel;
  59. // Figure out the Hyperlink type and destination
  60. if (_externalRel == null) {
  61. // If it has a location, it's internal
  62. if (ctHyperlink.getLocation() != null) {
  63. _type = HyperlinkType.DOCUMENT;
  64. _location = ctHyperlink.getLocation();
  65. } else if (ctHyperlink.getId() != null) {
  66. throw new IllegalStateException("The hyperlink for cell "
  67. + ctHyperlink.getRef() + " references relation "
  68. + ctHyperlink.getId() + ", but that didn't exist!");
  69. } else {
  70. // hyperlink is internal and is not related to other parts
  71. _type = HyperlinkType.DOCUMENT;
  72. }
  73. } else {
  74. URI target = _externalRel.getTargetURI();
  75. _location = target.toString();
  76. if (ctHyperlink.getLocation() != null) {
  77. // URI fragment
  78. _location += "#" + ctHyperlink.getLocation();
  79. }
  80. // Try to figure out the type
  81. if (_location.startsWith("http://") || _location.startsWith("https://")
  82. || _location.startsWith("ftp://")) {
  83. _type = HyperlinkType.URL;
  84. } else if (_location.startsWith("mailto:")) {
  85. _type = HyperlinkType.EMAIL;
  86. } else {
  87. _type = HyperlinkType.FILE;
  88. }
  89. }
  90. }
  91. /**
  92. * Create a new XSSFHyperlink. This method is for Internal use only.
  93. * XSSFHyperlinks can be created by {@link XSSFCreationHelper}.
  94. * See the <a href="https://poi.apache.org/spreadsheet/quick-guide.html#Hyperlinks">spreadsheet quick-guide</a>
  95. * for an example.
  96. *
  97. * @param other the hyperlink to copy
  98. */
  99. @Internal //FIXME: change to protected if/when SXSSFHyperlink class is created
  100. public XSSFHyperlink(Hyperlink other) {
  101. if (other instanceof XSSFHyperlink) {
  102. XSSFHyperlink xlink = (XSSFHyperlink) other;
  103. _type = xlink.getType();
  104. _location = xlink._location;
  105. _externalRel = xlink._externalRel;
  106. _ctHyperlink = (CTHyperlink) xlink._ctHyperlink.copy();
  107. }
  108. else {
  109. _type = other.getType();
  110. _location = other.getAddress();
  111. _externalRel = null;
  112. _ctHyperlink = CTHyperlink.Factory.newInstance();
  113. _ctHyperlink.setDisplay(other.getLabel());
  114. setFirstColumn(other.getFirstColumn());
  115. setLastColumn(other.getLastColumn());
  116. setFirstRow(other.getFirstRow());
  117. setLastRow(other.getLastRow());
  118. }
  119. }
  120. /**
  121. * @return the underlying CTHyperlink object
  122. */
  123. @Internal
  124. public CTHyperlink getCTHyperlink() {
  125. return _ctHyperlink;
  126. }
  127. /**
  128. * Do we need to a relation too, to represent
  129. * this hyperlink?
  130. */
  131. public boolean needsRelationToo() {
  132. return (_type != HyperlinkType.DOCUMENT);
  133. }
  134. /**
  135. * Generates the relation if required
  136. */
  137. protected void generateRelationIfNeeded(PackagePart sheetPart) {
  138. if (_externalRel == null && needsRelationToo()) {
  139. // Generate the relation
  140. PackageRelationship rel =
  141. sheetPart.addExternalRelationship(_location, XSSFRelation.SHEET_HYPERLINKS.getRelation());
  142. // Update the r:id
  143. _ctHyperlink.setId(rel.getId());
  144. }
  145. }
  146. /**
  147. * Return the type of this hyperlink
  148. *
  149. * @return the type of this hyperlink
  150. */
  151. @Override
  152. public HyperlinkType getType() {
  153. return _type;
  154. }
  155. /**
  156. * Get the address of the cell this hyperlink applies to, e.g. A55
  157. */
  158. public String getCellRef() {
  159. return _ctHyperlink.getRef();
  160. }
  161. /**
  162. * Hyperlink address. Depending on the hyperlink type it can be URL, e-mail, path to a file.
  163. * The is the hyperlink target.
  164. *
  165. * @return the address of this hyperlink
  166. */
  167. @Override
  168. public String getAddress() {
  169. return _location;
  170. }
  171. /**
  172. * Return text label for this hyperlink
  173. *
  174. * @return text to display
  175. */
  176. @Override
  177. public String getLabel() {
  178. return _ctHyperlink.getDisplay();
  179. }
  180. /**
  181. * Location within target. If target is a workbook (or this workbook) this shall refer to a
  182. * sheet and cell or a defined name. Can also be an HTML anchor if target is HTML file.
  183. *
  184. * @return location
  185. */
  186. public String getLocation() {
  187. return _ctHyperlink.getLocation();
  188. }
  189. /**
  190. * Sets text label for this hyperlink
  191. *
  192. * @param label text label for this hyperlink
  193. */
  194. @Override
  195. public void setLabel(String label) {
  196. _ctHyperlink.setDisplay(label);
  197. }
  198. /**
  199. * Location within target. If target is a workbook (or this workbook) this shall refer to a
  200. * sheet and cell or a defined name. Can also be an HTML anchor if target is HTML file.
  201. *
  202. * @param location - string representing a location of this hyperlink
  203. */
  204. public void setLocation(String location) {
  205. _ctHyperlink.setLocation(location);
  206. }
  207. /**
  208. * Hyperlink address. Depending on the hyperlink type it can be URL, e-mail, path to a file
  209. * This is the hyperlink target.
  210. *
  211. * @param address - the address of this hyperlink
  212. */
  213. @Override
  214. public void setAddress(String address) {
  215. validate(address);
  216. _location = address;
  217. //we must set location for internal hyperlinks
  218. if (_type == HyperlinkType.DOCUMENT) {
  219. setLocation(address);
  220. }
  221. }
  222. @SuppressWarnings("fall-through")
  223. private void validate(String address) {
  224. switch (_type) {
  225. // email, path to file and url must be valid URIs
  226. case EMAIL:
  227. case FILE:
  228. case URL:
  229. try {
  230. new URI(address);
  231. } catch (URISyntaxException e) {
  232. throw new IllegalArgumentException("Address of hyperlink must be a valid URI", e);
  233. }
  234. break;
  235. case DOCUMENT:
  236. // currently not evaluating anything.
  237. break;
  238. default:
  239. throw new IllegalStateException("Invalid Hyperlink type: " + _type);
  240. }
  241. }
  242. /**
  243. * Assigns this hyperlink to the given cell reference
  244. */
  245. @Internal
  246. public void setCellReference(String ref) {
  247. _ctHyperlink.setRef(ref);
  248. }
  249. @Internal
  250. public void setCellReference(CellReference ref) {
  251. setCellReference(ref.formatAsString());
  252. }
  253. private CellReference buildFirstCellReference() {
  254. return buildCellReference(false);
  255. }
  256. private CellReference buildLastCellReference() {
  257. return buildCellReference(true);
  258. }
  259. private CellReference buildCellReference(boolean lastCell) {
  260. String ref = _ctHyperlink.getRef();
  261. if (ref == null) {
  262. ref = "A1";
  263. }
  264. if (ref.contains(":")) {
  265. AreaReference area = new AreaReference(ref, SpreadsheetVersion.EXCEL2007);
  266. return lastCell ? area.getLastCell() : area.getFirstCell();
  267. }
  268. return new CellReference(ref);
  269. }
  270. /**
  271. * Return the column of the first cell that contains the hyperlink
  272. *
  273. * @return the 0-based column of the first cell that contains the hyperlink
  274. */
  275. @Override
  276. public int getFirstColumn() {
  277. return buildFirstCellReference().getCol();
  278. }
  279. /**
  280. * Return the column of the last cell that contains the hyperlink
  281. *
  282. * @return the 0-based column of the last cell that contains the hyperlink
  283. */
  284. @Override
  285. public int getLastColumn() {
  286. return buildLastCellReference().getCol();
  287. }
  288. /**
  289. * Return the row of the first cell that contains the hyperlink
  290. *
  291. * @return the 0-based row of the cell that contains the hyperlink
  292. */
  293. @Override
  294. public int getFirstRow() {
  295. return buildFirstCellReference().getRow();
  296. }
  297. /**
  298. * Return the row of the last cell that contains the hyperlink
  299. *
  300. * @return the 0-based row of the last cell that contains the hyperlink
  301. */
  302. @Override
  303. public int getLastRow() {
  304. return buildLastCellReference().getRow();
  305. }
  306. /**
  307. * Set the column of the first cell that contains the hyperlink
  308. *
  309. * @param col the 0-based column of the first cell that contains the hyperlink
  310. */
  311. @Override
  312. public void setFirstColumn(int col) {
  313. int lastColumn = getLastColumn();
  314. if (col > lastColumn) lastColumn = col;
  315. String firstCellRef = CellReference.convertNumToColString(col) + (getFirstRow() + 1);
  316. String lastCellRef = CellReference.convertNumToColString(lastColumn) + (getLastRow() + 1);
  317. setCellRange(firstCellRef + ":" + lastCellRef);
  318. }
  319. /**
  320. * Set the column of the last cell that contains the hyperlink.
  321. *
  322. * @param col the 0-based column of the last cell that contains the hyperlink
  323. */
  324. @Override
  325. public void setLastColumn(int col) {
  326. int firstColumn = getFirstColumn();
  327. if (col < firstColumn) firstColumn = col;
  328. String firstCellRef = CellReference.convertNumToColString(firstColumn) + (getFirstRow() + 1);
  329. String lastCellRef = CellReference.convertNumToColString(col) + (getLastRow() + 1);
  330. setCellRange(firstCellRef + ":" + lastCellRef);
  331. }
  332. /**
  333. * Set the row of the first cell that contains the hyperlink
  334. *
  335. * @param row the 0-based row of the first cell that contains the hyperlink
  336. */
  337. @Override
  338. public void setFirstRow(int row) {
  339. int lastRow = getLastRow();
  340. if (row > lastRow) lastRow = row;
  341. String firstCellRef = CellReference.convertNumToColString(getFirstColumn()) + (row + 1);
  342. String lastCellRef = CellReference.convertNumToColString(getLastColumn()) + (lastRow + 1);
  343. setCellRange(firstCellRef + ":" + lastCellRef);
  344. }
  345. /**
  346. * Set the row of the last cell that contains the hyperlink.
  347. *
  348. * @param row the 0-based row of the last cell that contains the hyperlink
  349. */
  350. @Override
  351. public void setLastRow(int row) {
  352. int firstRow = getFirstRow();
  353. if (row < firstRow) firstRow = row;
  354. String firstCellRef = CellReference.convertNumToColString(getFirstColumn()) + (firstRow + 1);
  355. String lastCellRef = CellReference.convertNumToColString(getLastColumn()) + (row + 1);
  356. setCellRange(firstCellRef + ":" + lastCellRef);
  357. }
  358. private void setCellRange(String range) {
  359. AreaReference ref = new AreaReference(range, SpreadsheetVersion.EXCEL2007);
  360. if(ref.isSingleCell()) {
  361. setCellReference(ref.getFirstCell());
  362. } else {
  363. setCellReference(ref.formatAsString());
  364. }
  365. }
  366. /**
  367. * @return additional text to help the user understand more about the hyperlink
  368. */
  369. public String getTooltip() {
  370. return _ctHyperlink.getTooltip();
  371. }
  372. /**
  373. * @param text additional text to help the user understand more about the hyperlink
  374. */
  375. public void setTooltip(String text) {
  376. _ctHyperlink.setTooltip(text);
  377. }
  378. /**
  379. * @return a new XSSFHyperlink based on this
  380. * @since POI 5.1.0
  381. */
  382. @Override
  383. public Duplicatable copy() {
  384. return new XSSFHyperlink(this);
  385. }
  386. }